.NET Regex - Replace multiple characters at once without overwriting? - c#

I'm implementing a c# program that should automatize a Mono-alphabetic substitution cipher.
The functionality i'm working on at the moment is the simplest one: The user will provide a plain text and a cipher alphabet, for example:
Plain text(input): THIS IS A TEST
Cipher alphabet: A -> Y, H -> Z, I -> K, S -> L, E -> J, T -> Q
Cipher Text(output): QZKL KL QJLQ
I thought of using regular expressions since I've been programming in perl for a while, but I'm encountering some problems on c#.
First I would like to know if someone would have a suggestion for a regular expression that would replace all occurrence of each letter by its corresponding cipher letter (provided by user) at once and without overwriting anything.
Example:
In this case, user provides plaintext "TEST", and on his cipher alphabet, he wishes to have all his T's replaced with E's, E's replaced with Y and S replaced with J. My first thought was to substitute each occurrence of a letter with an individual character and then replace that character by the cipherletter corresponding to the plaintext letter provided.
Using the same example word "TEST", the steps taken by the program to provide an answer would be:
replace T's with (lets say) #
replace E's with #
replace S's with &
Replace # with E, # with Y, & with j
Output = EYJE
This solution doesn't seem to work for large texts.
I would like to know if anyone can think of a single regular expression that would allow me to replace each letter in a given text by its corresponding letter in a 26-letter cipher alphabet without the need of splitting the task in an intermediate step as I mentioned.
If it helps visualize the process, this is a print screen of my GUI for the program: alt text http://img43.imageshack.us/img43/2118/11618743.jpg

You could also make a map of source to destination characters, then simply loop through the string and replace as you go:
Dictionary<char, char> replacements = new Dictionary<char, char>();
// set up replacement chars, like this
replacements['T'] = '#';
replacements['E'] = '#';
replacements['S'] = '&';
replacements['#'] = 'E';
replacements['#'] = 'Y';
replacements['&'] = 'J';
// actually perform the replacements
char[] result = new char[source.Length];
for (int i = 0; i < result.Length; i++) {
result[i] = replacements[source[i]];
}
return new string(result);

I don't think regular expressions are the right tool here. In Perl you would use the transliteration feature tr/TES/EYJ/. C# doesn't have this but you can do it by using a StringBuilder and looking at each character individually.
private static string Translate(string input, string from, string to)
{
StringBuilder sb = new StringBuilder();
foreach (char ch in input)
{
int i = from.IndexOf(ch);
if (i < 0)
{
sb.Append(ch);
}
else if (i < to.Length)
{
sb.Append(to[i]);
}
}
return sb.ToString();
}
The source code is a modified version of this answer from this similar question. The answers there show some other ways of doing this.

Related

Moving the first char in a string to the send of the string using a method. C#

I know there are a lot of similar questions asked, and I've looked over those, but I still can't figure out my solution.
I'm trying to write a method that takes the first character of an inputted string and moves it to the back, then I can add additional characters if needed.
Basically if the input is Hello the output would be elloH + "whatever." I hope that makes sense.
As proof that I'm just not being lazy, here is the rest of the source code for the other parts of what I am working on. It all works, I just don't know where to begin with the last part.
Thanks for looking and thanks for the help!
private string CaseSwap(string str)//method for swaping cases
{
string result = ""; //create blank var
foreach (var c in str)
if (char.IsUpper(c)) //find uppers
result += char.ToLower(c); //change to lower
else
result += char.ToUpper(c); //all other lowers changed to upper
str = result; //assign var to str
return str; //return string to method
}
private string Reverse(string str)//method for reversing string
{
char[] revArray = str.ToCharArray(); //copy into an array
Array.Reverse(revArray); //reverse the array
return new string(revArray); //return the new string
}
private string Latin(string str)//method for latin
{
}
}
}
If you want to move first character to the end of string, then you can try below
public string MoveFirstCharToEnd(string str, string whateverStr="")
{
if(string.IsNullOrEmpty(str))
return str;
string result = str.Substring(1) + str[0] + whateverStr;
return result;
}
Note: I added whateverStr as an optional parameter, so that it can support only moving first character to the end and also it supports concatenating extra string to the result.
String.Substring(Int32):
Retrieves a substring from this instance. The substring starts at a
specified character position and continues to the end of the string.
Why not just take the 1st char and combine it with the rest of the string? E.g.
Hello
^^ ^
|| |
|Substring(1) - rest of the string (substring starting from 1)
|
value[0] - first character
Code:
public static string Rotate(string value) => string.IsNullOrEmpty(value)
? value
: $"{value.Substring(1)}{value[0]}";
Generalized implementation for arbitrary rotation (either positive or negative):
public static string Rotate(string value, int count = 1) {
if (string.IsNullOrWhiteSpace(value))
return value;
return string.Concat(Enumerable
.Range(0, value.Length)
.Select(i => value[(i + count % value.Length + value.Length) % value.Length]));
}
You can simplify your current implementation with a help of Linq
using System.Linq;
...
private static string CaseSwap(string value) =>
string.Concat(value.Select(c => char.IsUpper(c)
? char.ToLower(c)
: char.ToUpper(c)));
private static string Reverse(string value) =>
string.Concat(value.Reverse());
You can try to get the first character of a string with the String.Substring(int startPosition, int length) method . With this method you can also get the rest of your text starting from position 1 (skip the first character). When you have these 2 pieces, you can concat them.
Don't forget to check for empty strings, this can be done with the String.IsNullOrEmpty(string text) method.
public static string RemoveAndConcatFirstChar(string text){
if (string.IsNullOrEmpty(text)) return "";
return text.Substring(1) + text.Substring(0,1);
}
Appending multiple characters to a string is inefficient due to the number of string objects allocated, which is not just memory intensive it's also slow. There's a reason we have StringBuilder and other such options available to us, like working with char[]s.
Here's a fairly quick method that for rotating a string left one character (moving the first character to the end):
string RotateLeft(string source)
{
var chars = source.ToCharArray();
var initial = chars[0];
Array.Copy(chars, 1, chars, 0, chars.Length - 1);
chars[^1] = initial;
return new String(chars);
}
Sadly we can't do that in-place in the string itself since they're immutable, so there's no avoiding the temporary array and string construction at the end.
Based on the fact that you called the method Latin(...) and the bit of the question where you said: "Basically if the input is Hello the output would be elloH + "whatever."... I'm assuming that you're writing a Pig Latin translation. If that's the case, you're going to need a bit more.
Pig Latin is a slightly tricky problem because it's based on the sound of the word, not the letters. For example, onto becomes ontohay (or variants thereof) while one becomes unway because the word is pronounced the same as won (with a u to capture the vowel pronunciation correctly). Phonetic operations on English is quite annoying because of all the variations with silent and implied initial letters. And don't even get me started on pseudo-vowels like y.
Special cases aside, the most common rules of Pig Latin translation code appear to be as follows:
Words starting with a single consonant followed by a vowel: move the consonant to the end and append ay.
Words starting with a pair of consonants followed by a vowel: move the consonant pair to the end and append ay.
Words that start with a vowel: append hay, yay, tay, etc.
That third one is a bit difficult since choosing the right suffix is a matter of what makes the result easiest to say... which code can't really decide all that easily. Just pick one and go with that.
Of course there are plenty of words that don't fit those rules. Anything starting with a consonant triplet for example (Christmas being the first that came to mind, followed shortly by strip... and others). Pseudo-vowels like y mess things up (cry for instance). And of course the ever-present problem of correctly representing the initial vowel sounds when you've stripped context: won is converted to un-way vocally, so rendering it as on-way in text is a little bit wrong. Same with word, whose Pig Latin version is pronounced erd-way.
For a simple first pass though... just follow the rules, treating y as a consonant if it's the first letter and as a vowel in the second or third spots.
And since this is so often a homework problem, I'm going to stop here and let you play with it for a bit. Just in case :P
(Oh, and don't forget to preserve the case of your first character just in case you're working on a capitalized word. Latin should become Atinlay, not atinLay. Just saying.)

How to replace one character with multiple characters c#

I am doing some exapmples for my exam, and i find a problem replacing string with other string. I know how to replace one character in string to others or revert a string.
We have a one string
s="*****\n***\n****"
and we need to replace all "*" with "asd", and it should look like this
s="asdas\ndas\ndasd"
One possible approach is to write a for loop and check if the character is a * an keep track using a counter how many replacements have already been done.
Make use of the modulo % to see which character you need for the replacement and use a StringBuilder to add the characters.
For example
string s="*****\n***\n****";
StringBuilder sb = new StringBuilder();
string subj = "asd";
int counter = 0;
for (int i = 0; i < s.Length; i++) {
if (s[i] == '*') {
sb.Append(subj[counter % subj.Length]);
counter++;
continue;
}
sb.Append(s[i]);
}
Console.WriteLine(sb.ToString());
Result
asdas
das
dasd
C# demo
This does sound like a Homework assignment and with those we avoid providing full code. It is way to important for you to do the coding yourself. But it comes down to
Itterate over the string until you run out of '*' characters:
Replace the first '*' with 'a'.
Replace the first '*' with 's'.
Replace teh first '*' with 'd'
Restart at 1
There is one important thing to note with strings: Strings are inmutable. You can not modify a string, only create one with a different value. Each replace will create a new, dead string in memory. For this scope it does not mater, but for anything larger it may. Consider changing a Char[] or StringBuilder rather then a raw string, the recreate a string.

Using string.ToUpper on substring

Have an assignment to allow a user to input a word in C# and then display that word with the first and third characters changed to uppercase. Code follows:
namespace Capitalizer
{
class Program
{
static void Main(string[] args)
{
string text = Console.ReadLine();
char[] delimiterChars = { ' ' };
string[] words = text.Split(delimiterChars);
string Upper = text.ToUpper();
Console.WriteLine(Upper);
Console.ReadKey();
}
}
}
This of course generates the entire word in uppercase, which is not what I want. I can't seem to make text.ToUpper(0,2) work, and even then that'd capitalize the first three letters. Only solution I can think of now that would make the word appear on one line (and I don't know if it works) is to move the capitalized letters and lowercase letters into a character array and try to get that to print all values in a modified order.
The simplest way I can think of to address your exact question as described — to convert to upper case the first and third characters of the input — would be something like the following:
StringBuilder sb = new StringBuilder(text);
sb[0] = char.ToUpper(sb[0]);
sb[2] = char.ToUpper(sb[2]);
text = sb.ToString();
The StringBuilder class is essentially a mutable string object, so when doing these kinds of operations is the most fluid way to approach the problem, as it provides the most straightforward conversions to and from, as well as the full range of string operations. Changing individual characters is easy in many data structures, but insertions, deletions, appending, formatting, etc. all also come with StringBuilder, so it's a good habit to use that versus other approaches.
But frankly, it's hard to see how that's a useful operation. I can't help but wonder if you have stated the requirements incorrectly and there's something more to this question than is seen here.
You could use LINQ:
var upperCaseIndices = new[] { 0, 2 };
var message = "hello";
var newMessage = new string(message.Select((c, i) =>
upperCaseIndices.Contains(i) ? Char.ToUpper(c) : c).ToArray());
Here is how it works. message.Select (inline LINQ query) selects characters from message one by one and passes into selector function:
upperCaseIndices.Contains(i) ? Char.ToUpper(c) : c
written as C# ?: shorthand syntax for if. It reads as "If index is present in the array, then select upper case character. Otherwise select character as is."
(c, i) => condition
is a lambda expression. See also:
Understand Lambda Expressions in 3 minutes
The rest is very simple - represent result as array of characters (.ToArray()), and create a new string based off that (new string(...)).
Only solution I can think of now that would make the word appear on one line (and I don't know if it works) is to move the capitalized letters and lowercase letters into a character array and try to get that to print all values in a modified order.
That seems a lot more complicated than necessary. Once you have a character array, you can simply change the elements of that character array. In a separate function, it would look something like
string MakeFirstAndThirdCharacterUppercase(string word) {
var chars = word.ToCharArray();
chars[0] = chars[0].ToUpper();
chars[2] = chars[2].ToUpper();
return new string(chars);
}
My simple solution:
string text = Console.ReadLine();
char[] delimiterChars = { ' ' };
string[] words = text.Split(delimiterChars);
foreach (string s in words)
{
char[] chars = s.ToCharArray();
chars[0] = char.ToUpper(chars[0]);
if (chars.Length > 2)
{
chars[2] = char.ToUpper(chars[2]);
}
Console.Write(new string(chars));
Console.Write(' ');
}
Console.ReadKey();

Multiple occurrences of text in a string

How could I use a for loop to go through each iteration of a given phrase in a string? For instance, say I had the following string:
Hey, this is an example string. A string is a collection of characters.
And every time there was an "is", I wanted to assign the three characters after it to a new string. I understand how to do that ONCE, but I'm trying to figure out how a for loop could be used to go through multiple instances of the same word.
If you must use a for-loop for whatever reason, you can replace the relevant part of the code provided by ja72 with:
for (int i = 0; i < text.Length; i++)
{
if (text[i] == 'i' && text[i+1] == 's')
sb.Append(text.Substring(i + 2, 3));
}
Unfortunately, I don't have enough reputation to add this as a comment here, hence posting it as an answer!
Is this what you want?
static void Main(string[] args)
{
string text=#"Hey, this is an example string. A string is a collection of characters.";
StringBuilder sb=new StringBuilder();
int i=-1;
while ((i=text.IndexOf("is", i+1))>=0)
{
sb.Append(text.Substring(i+2, 3));
}
string result=sb.ToString();
}
//result " is an a "
You can use a regex like this:
Regex re = new Regex("(?:is)(.{3})");
This regex looks for is (?:is), and takes the next three characters (.{3})
Then you use the regex to find all matches: Regex.Matches(). This will return a match for each is found in the string, followed by 3 characters. Each match has two groups:
Group 0: that includes is and the next three characters
Group 1: which includes the next thress characters
Matches matches = re.Matches("Hey, this is an example string. A string is a collection of characters.");
StringBuilder sb = new StringBuilder();
foreach (Match m in matches)
{
sb.Append(m.Groups1.Value);
}
Using Regex is much faster than looping through the characters of the string. Even more if you use RegexOptions.Compiled in your regex constructor: Regex Constructor (String, RegexOptions)

Display string as underscores but keeping value

I am writing a basic hangman game. I want to store a string Word as the word to be found by the user. My problem is that I want to display this word as underscores (like you do in a paper hangman) but keep it's string value. A friend of mine said this could be done via Regex but researching it doesn't seem to help me.
As suggested by Farinha, but using HashSet
Some importang points: we are using a HashSet because in the end the letters can have two states: discovered (present in the HS) or not discovered (not present). We initialize the HashSet passing as a parameter StringComparer.InvariantCultureIgnoreCase because we consider L and l to be the same thing. To use the StringComparer.InvariantCultureIgnoreCase we have to use an HashSet<string> (an HS of strings) instead of an HashSet<char> (an HS of chars). So when using the discovered.Contains() we have to convert the c char in a string with a ToString
static string ConvertWord(string word, HashSet<string> discovered)
{
StringBuilder sb = new StringBuilder(word.Length);
foreach (char c in word)
{
if (discovered.Contains(c.ToString()))
{
sb.Append(c);
}
else
{
sb.Append('_');
}
}
return sb.ToString();
}
HashSet<string> discovered = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
// The "secret" word
string word = "Hello world";
// How to add letters to the ones discovered
discovered.Add("l");
// The word ready to be shown
string convertWord = ConvertWord(word, discovered);
We could have done the ConvertWord in much less characters, but for today it's enough :-)
Ok... I'll give you an example, in C# 4.0:
static string ConvertWord(string word, HashSet<string> discovered)
{
return string.Concat(word.Select(p => discovered.Contains(p.ToString()) ? p : '_'));
}
I am not sure if this is exactly what you want, but you can take a look if it is helpful.
The game was simulated by sed and echo. of course, you can use variable for the secret word, not echo as plain text.
say, my secret word is "memory"
#user gives a letter "a"
kent$ echo "memory"|sed 's/[^a]/_/g'
______
#user gives a letter "r"
kent$ echo "memory"|sed 's/[^ar]/_/g'
____r_
#user gives a letter "m"
kent$ echo "memory"|sed 's/[^arm]/_/g'
m_m_r_
#user gives a letter "o"
kent$ echo "memory"|sed 's/[^armo]/_/g'
m_mor_
#user gives a letter "x"
kent$ echo "memory"|sed 's/[^armox]/_/g'
m_mor_
#user gives a letter "y" then "e"
kent$ echo "memory"|sed 's/[^armoxy]/_/g'
m_mory
kent$ echo "memory"|sed 's/[^armoxye]/_/g'
memory
Using a Regex doesn't seem like a good approach. I would instead grab the word, break it down into characters, and build a Dictionary<char,bool> where the bool indicates if the letter has been discovered yet. Then you an look through the Dictionary and for each item display the char if the letter has been discovered, or an underscore if not.
// build the Dictionary
string originalWord = "Stack Overflow";
Dictionary<char, bool> dict = new Dictionary<char, bool>();
for(int i = 0; i < originalWord.Length; i++)
dict.Add(originalWord[i], false);
// output the current state
string current = "";
foreach(KeyValuePair<char, bool> pair in dict)
{
current += pair.Value ? pair.Key.ToString() : "_";
}
Why not using a textbox and having its entry type as password, then set his password character as 'underscore'
I wouldn't bother with regex - you can write a fairly simple function to take an array of characters which represent the guesses made and return the representation of the word as required:
string word = "AMBULANCE";
public string Hangman(char[] guesses)
{
char[] output = new char[word.Length];
for (int i = 0; i < word.Length; i++)
{
if (guesses.Contains(word[i]))
{
output[i] = word[i];
}
}
return new String(output).Replace('\0','_');
}
and then call it using something like:
char[] guesses = new char[] { 'A', 'E' };
Console.WriteLine(Hangman(guesses));
Console.ReadLine();

Categories

Resources