Replace each character of string with new character string - c#

The c# program I'm developing checks the first character of user input and if it starts with . (dot) I want to replace each character of user input with pacified character string while the user is writing, but I'm getting the error
Index out of bounds exception
My code:
if (textBox1.Text.StartWith(".")) {
string MyText = "Hello World";
int x = 0;
string NewText;
while (x <= MyText.Length) {
NewText = textBox1.Text.Replace(textBox1.Text[x], MyText[x]);
TextBox1.Text = NewText;
x++;
}
}

You're overrunning the bounds of the string, replace:
while (x <= MyText.Length) {
with
while (x < MyText.Length) {

while(x < MyText.Length)
or
while(x <= MyText.Length - 1)
If array has length = x, its last index is x-1, because array starts from 0 index

If I understand you right (there're no samples in question), I suggest using Linq which is straitforward; try using modular arimethics - index % MyText.Length to avoid index problems
string source = ".My Secret Message for Test";
string MyText = "Hello World";
// If user input starts with dot
if (source.StartsWith("."))
source = string.Concat(source
.Select((c, index) => MyText[index % MyText.Length]));
TextBox1.Text = source;
Outcome:
Hello WorldHello WorldHello

First of all as #Daniell89 said:
use
while(x < MyText.Length)
Secondly:
You use x as an index not only for MyText but for textBox1.Text too. So you need to check that it is long enough.
you can do something like this:
while (x < Math.Min(MyText.Length, textBox1.Text.Length)
{
NewText = textBox1.Text.Replace(textBox1.Text[x], MyText[x]);
TextBox1.Text = NewText;
x++;
}
But I think it would be better to use for statement here.

Related

Splitting the letters based on position of letter

I am trying to spilt a string word into two strings based on the letter position. The two strings are even and odd. I manage to read the string and used a for loop but the conditional operator is not working and give me the error below. What did I do wrong?
Example: The string word is pole
Even position string - oe
Odd position string - pl
Error
Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
string word = Console.ReadLine();
for(int i = 0; i < word.Length; i++)
{
string even = "";
string odd = "";
((i % 2 == 0) ? even += word[i]: odd += word[i]);
}
You could use the discard operator as the following.
string word = Console.ReadLine();
string even = "";
string odd = "";
for(int i = 0; i < word.Length; i++)
{
var _ = ((i % 2 == 0) ? even += word[i]: odd += word[i]);
}
Couple of points to note here.
You need to declare the odd,even variables outside the loop, otherwise it would be recreated for each iteration of loop
Remember the string is immutable.You could also consider the StringBuilder class.
I am not that familiar with the ? operator, however, in my research, it appears it wants something like below…
((i % 2 == 0) ? ref even : ref odd) += word[i];
Unfortunately, even with this change, the even and odd variables are getting “reset” to empty with each iteration of the for loop with…
string even = "";
string odd = "";
If the goal is to concatenate the values, you do NOT want to create new even and odd variables with each iteration. So you should move those declarations “outside” the for loop. Something like…
string word = Console.ReadLine();
string even = "";
string odd = "";
for (int i = 0; i < word.Length; i++) {
((i % 2 == 0) ? ref even : ref odd) += word[i];
}
You use conditional operator to assign values inside of it. It is not allowed.
The correct for-loop is:
for (int i = 0; i < word.Length; i++)
{
if (i % 2 == 0)
{
even += word[i];
}
else
{
odd += word[i];
};
}
You can also use LINQ to get the expected result:
string word = Console.ReadLine();
string even = string.Concat(word.Where((c,i) => i % 2 == 0));
string odd = string.Concat(word.Where((c,i) => i % 2 == 1));
Online demo: https://dotnetfiddle.net/ePWHnp

C#: Need to split a string into a string[] and keeping the delimiter (also a string) at the beginning of the string

I think I am too dumb to solve this problem...
I have some formulas which need to be "translated" from one syntax to another.
Let's say I have a formula that goes like that (it's a simple one, others have many "Ceilings" in it):
string formulaString = "If([Param1] = 0, 1, Ceiling([Param2] / 0.55) * [Param3])";
I need to replace "Ceiling()" with "Ceiling(; 1)" (basically, insert "; 1" before the ")").
My attempt is to split the fomulaString at "Ceiling(" so I am able to iterate through the string array and insert my string at the correct index (counting every "(" and ")" to get the right index)
What I have so far:
//splits correct, but loses "CEILING("
string[] parts = formulaString.Split(new[] { "CEILING(" }, StringSplitOptions.None);
//splits almost correct, "CEILING(" is in another group
string[] parts = Regex.Split(formulaString, #"(CEILING\()");
//splits almost every letter
string[] parts = Regex.Split(formulaString, #"(?=[(CEILING\()])");
When everything is done, I concat the string so I have my complete formula again.
What do I have to set as Regex pattern to achieve this sample? (Or any other method that will help me)
part1 = "If([Param1] = 0, 1, ";
part2 = "Ceiling([Param2] / 0.55) * [Param3])";
//part3 = next "CEILING(" in a longer formula and so on...
As I mention in a comment, you almost got it: (?=Ceiling). This is incomplete for your use case unfortunately.
I need to replace "Ceiling()" with "Ceiling(; 1)" (basically, insert "; 1" before the ")").
Depending on your regex engine (for example JS) this works:
string[] parts = Regex.Split(formulaString, #"(?<=Ceiling\([^)]*(?=\)))");
string modifiedFormula = String.join("; 1", parts);
The regex
(?<=Ceiling\([^)]*(?=\)))
(?<= ) Positive lookbehind
Ceiling\( Search for literal "Ceiling("
[^)] Match any char which is not ")" ..
* .. 0 or more times
(?=\)) Positive lookahead for ")", effectively making us stop before the ")"
This regex is a zero-assertion, therefore nothing is lost and it will cut your strings before the last ")" in every "Ceiling()".
This solution would break whenever you have nested "Ceiling()". Then your only solution would be writing your own parser for the same reasons why you can't parse markup with regex.
Regex.Replace(formulaString, #"(?<=Ceiling\()(.*?)(?=\))","$1; 1");
Note: This will not work for nested "Ceilings", but it does for Ceiling(), It will also not work fir Ceiling(AnotherFunc(x)). For that you need something like:
Regex.Replace(formulaString, #"(?<=Ceiling\()((.*\((?>[^()]+|(?1))*\))*|[^\)]*)(\))","$1; 1$3");
but I could not get that to work with .NET, only in JavaScript.
This is my solution:
private string ConvertCeiling(string formula)
{
int ceilingsCount = formula.CountOccurences("Ceiling(");
int startIndex = 0;
int bracketCounter;
for (int i = 0; i < ceilingsCount; i++)
{
startIndex = formula.IndexOf("Ceiling(", startIndex);
bracketCounter = 0;
for (int j = 0; j < formula.Length; j++)
{
if (j < startIndex) continue;
var c = formula[j];
if (c == '(')
{
bracketCounter++;
}
if (c == ')')
{
bracketCounter--;
if (bracketCounter == 0)
{
// found end
formula = formula.Insert(j, "; 1");
startIndex++;
break;
}
}
}
}
return formula;
}
And CountOccurence:
public static int CountOccurences(this string value, string parameter)
{
int counter = 0;
int startIndex = 0;
int indexOfCeiling;
do
{
indexOfCeiling = value.IndexOf(parameter, startIndex);
if (indexOfCeiling < 0)
{
break;
}
else
{
startIndex = indexOfCeiling + 1;
counter++;
}
} while (true);
return counter;
}

How do I alternate the case in a string?

I need to alternate the case in a sentence and I don't know how to.
For example:
thequickbrownfoxjumpsoverthelazydog
to
GoDyZaLeHtReVoSpMuJxOfNwOrBkCiUqEhT
this is my code so far
Console.WriteLine("Please enter a sentence:");
string text = Console.ReadLine();
text = text.Replace(" ", "");
char[] reversed = text.ToCharArray();//String to char
Array.Reverse(reversed);//Reverses char
new string(reversed);//Char to string
Console.WriteLine(reversed);
Console.ReadLine();
Please note that there are no spaces for a reason as that's also part of the homework task.
A string is immutable, so, you need to convert it to a char[].
char[] characters = text.ToCharArray();
for (int i = 0; i < characters.Length; i+=2) {
characters[i] = char.ToUpper(characters[i]);
}
text = new string(characters);
There is no point to reverse your string. Just upper case your even number indexed characters in your string.
Remember, my culture is tr-TR and this String.ToUpper method works depends on your current thread culture. In this example, your output can be different than mine.
Here an example in LINQPad;
string s = "thequickbrownfoxjumpsoverthelazydog";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.Length; i++)
{
if (i % 2 == 0)
{
sb.Append(s[i].ToString().ToUpper());
}
else
{
sb.Append(s[i].ToString());
}
}
sb.ToString().Dump();
Output will be;
ThEqUiCkBrOwNfOxJuMpSoVeRtHeLaZyDoG
Another possible solution with LINQ can be done in one line like this:
string s = "thequickbrownfoxjumpsoverthelazydog";
string result = new String(s
// take each character
.ToCharArray()
// convert every character at even index to upper
.Select ((character, index) => (index % 2) == 0 ? Char.ToUpper(character) : character)
// back to array in order to create a string
.ToArray());
Console.WriteLine(result);
The output is:
ThEqUiCkBrOwNfOxJuMpSoVeRtHeLaZyDoG
This solution uses the indexed LINQ Select clause in order to access the current index and the value that is currently projected.
A one liner:
new string(myString.Select((c, i) => i % 2 == 0 ? char.ToUpper(c) : c).ToArray())
An extension method:
public static string AltCase(this string s)
{
return new string(s.Select((c, i) => i % 2 == 0 ? char.ToUpper(c) : c).ToArray());
}

Logical error in simple acronym generator

I have some kind of logical error in my program. Whenever I enter a phrase with 1 letter I get a ArgumentOutOfRange Exception, and whenever I enter a multiple letter word the textbox clears, displays "Apple" (the first value in my array) and does nothing else. Can anybody see the logical error in this?
string[] d = { "Apple", "Bass", "Cat", "Dog", "Ear", "Flamingo", "Gear", "Hat", "Infidel", "Jackrabbit", "Kangaroo", "Lathargic", "Monkey", "Nude", "Ozzymandis", "Python", "Queen", "Rat", "Sarcastic", "Tungston", "Urine", "Virginia", "Wool", "Xylophone", "Yo-yo", "Zebra", " " };
string var;
int len = 0;
private void button1_Click(object sender, EventArgs e)
{
var = textBox2.Text;
textBox1.Text = "";
for (int y = 0; y < var.Length; y++)
{
for (int x = 0; x < d.Length; x++)
{
if (d[x].ToUpper().Substring(0, 0) == var.ToUpper().Substring(len, len))
{
len = len + 1;
textBox1.Text = textBox1.Text + "\n" + d[x];
}
}
}
}
Substring(0, 0) is really pointless. This will always be an empty string.
Substring(len, len) also is a bad idea, because it will return a string of length len starting at index len. This is where you get your exception.
I assume, what you really want is the second parameter to be 1 in both cases. And that can be further simplified to an access via index:
d[x].ToUpper()[0] == var.ToUpper()[len]
You can do the same quite easily using LINQ:
private void button1_Click(object sender, EventArgs e)
{
var dict = d.ToDictionary(x => x.First(), x => x);
textBox1.Text = string.Join(Environment.NewLine, textBox2.Text.Select(x => dict[char.ToUpper(x)]));
}
To do it without LINQ I would suggest following:
for (int y = 0; y < input.Length; y++)
{
for (int x = 0; x < d.Length; x++)
{
if (char.ToUpper(d[x][0]) == char.ToUpper(input[y]))
{
result = result + "\n" + d[x];
}
}
}
Changes are:
you don't need len variable. Use y instead.
you don't need whole string uppercased. Use char.ToUpper static method instead.
you don't need string.Substring method. Use indexers instead.
This line explains the behavior.
if (d[x].ToUpper().Substring(0, 0) == var.ToUpper().Substring(len, len))
Second parameter of substring is string length. So on the left you always have an empty string. On the right you also have empty string when len==0 (that's why your code always picks Apple).
After that you change len, and repeat the loop. Then expression on the right is var.Substring(1,1) which gives you the error if your string is 1 character long. Because this reads - 1 symbol starting with 1 (which is second character of the string)
Apart from that, the purpose of the code is complete mystery, so there are definitely other errors.

How do I extract text that lies between parentheses (round brackets)?

I have a string User name (sales) and I want to extract the text between the brackets, how would I do this?
I suspect sub-string but I can't work out how to read until the closing bracket, the length of text will vary.
If you wish to stay away from regular expressions, the simplest way I can think of is:
string input = "User name (sales)";
string output = input.Split('(', ')')[1];
A very simple way to do it is by using regular expressions:
Regex.Match("User name (sales)", #"\(([^)]*)\)").Groups[1].Value
As a response to the (very funny) comment, here's the same Regex with some explanation:
\( # Escaped parenthesis, means "starts with a '(' character"
( # Parentheses in a regex mean "put (capture) the stuff
# in between into the Groups array"
[^)] # Any character that is not a ')' character
* # Zero or more occurrences of the aforementioned "non ')' char"
) # Close the capturing group
\) # "Ends with a ')' character"
Assuming that you only have one pair of parenthesis.
string s = "User name (sales)";
int start = s.IndexOf("(") + 1;
int end = s.IndexOf(")", start);
string result = s.Substring(start, end - start);
Use this function:
public string GetSubstringByString(string a, string b, string c)
{
return c.Substring((c.IndexOf(a) + a.Length), (c.IndexOf(b) - c.IndexOf(a) - a.Length));
}
and here is the usage:
GetSubstringByString("(", ")", "User name (sales)")
and the output would be:
sales
Regular expressions might be the best tool here. If you are not famililar with them, I recommend you install Expresso - a great little regex tool.
Something like:
Regex regex = new Regex("\\((?<TextInsideBrackets>\\w+)\\)");
string incomingValue = "Username (sales)";
string insideBrackets = null;
Match match = regex.Match(incomingValue);
if(match.Success)
{
insideBrackets = match.Groups["TextInsideBrackets"].Value;
}
string input = "User name (sales)";
string output = input.Substring(input.IndexOf('(') + 1, input.IndexOf(')') - input.IndexOf('(') - 1);
A regex maybe? I think this would work...
\(([a-z]+?)\)
using System;
using System.Text.RegularExpressions;
private IEnumerable<string> GetSubStrings(string input, string start, string end)
{
Regex r = new Regex(Regex.Escape(start) +`"(.*?)"` + Regex.Escape(end));
MatchCollection matches = r.Matches(input);
foreach (Match match in matches)
yield return match.Groups[1].Value;
}
int start = input.IndexOf("(") + 1;
int length = input.IndexOf(")") - start;
output = input.Substring(start, length);
Use a Regular Expression:
string test = "(test)";
string word = Regex.Match(test, #"\((\w+)\)").Groups[1].Value;
Console.WriteLine(word);
input.Remove(input.IndexOf(')')).Substring(input.IndexOf('(') + 1);
The regex method is superior I think, but if you wanted to use the humble substring
string input= "my name is (Jayne C)";
int start = input.IndexOf("(");
int stop = input.IndexOf(")");
string output = input.Substring(start+1, stop - start - 1);
or
string input = "my name is (Jayne C)";
string output = input.Substring(input.IndexOf("(") +1, input.IndexOf(")")- input.IndexOf("(")- 1);
var input = "12(34)1(12)(14)234";
var output = "";
for (int i = 0; i < input.Length; i++)
{
if (input[i] == '(')
{
var start = i + 1;
var end = input.IndexOf(')', i + 1);
output += input.Substring(start, end - start) + ",";
}
}
if (output.Length > 0) // remove last comma
output = output.Remove(output.Length - 1);
output : "34,12,14"
Here is a general purpose readable function that avoids using regex:
// Returns the text between 'start' and 'end'.
string ExtractBetween(string text, string start, string end)
{
int iStart = text.IndexOf(start);
iStart = (iStart == -1) ? 0 : iStart + start.Length;
int iEnd = text.LastIndexOf(end);
if(iEnd == -1)
{
iEnd = text.Length;
}
int len = iEnd - iStart;
return text.Substring(iStart, len);
}
To call it in your particular example you can do:
string result = ExtractBetween("User name (sales)", "(", ")");
I'm finding that regular expressions are extremely useful but very difficult to write. So, I did some research and found this tool that makes writing them so easy.
Don't shy away from them because the syntax is difficult to figure out. They can be so powerful.
This code is faster than most solutions here (if not all), packed as String extension method, it does not support recursive nesting:
public static string GetNestedString(this string str, char start, char end)
{
int s = -1;
int i = -1;
while (++i < str.Length)
if (str[i] == start)
{
s = i;
break;
}
int e = -1;
while(++i < str.Length)
if (str[i] == end)
{
e = i;
break;
}
if (e > s)
return str.Substring(s + 1, e - s - 1);
return null;
}
This one is little longer and slower, but it handles recursive nesting more nicely:
public static string GetNestedString(this string str, char start, char end)
{
int s = -1;
int i = -1;
while (++i < str.Length)
if (str[i] == start)
{
s = i;
break;
}
int e = -1;
int depth = 0;
while (++i < str.Length)
if (str[i] == end)
{
e = i;
if (depth == 0)
break;
else
--depth;
}
else if (str[i] == start)
++depth;
if (e > s)
return str.Substring(s + 1, e - s - 1);
return null;
}
I've been using and abusing C#9 recently and I can't help throwing in Spans even in questionable scenarios... Just for the fun of it, here's a variation on the answers above:
var input = "User name (sales)";
var txtSpan = input.AsSpan();
var startPoint = txtSpan.IndexOf('(') + 1;
var length = txtSpan.LastIndexOf(')') - startPoint;
var output = txtSpan.Slice(startPoint, length);
For the OP's specific scenario, it produces the right output.
(Personally, I'd use RegEx, as posted by others. It's easier to get around the more tricky scenarios where the solution above falls apart).
A better version (as extension method) I made for my own project:
//Note: This only captures the first occurrence, but
//can be easily modified to scan across the text (I'd prefer Slicing a Span)
public static string ExtractFromBetweenChars(this string txt, char openChar, char closeChar)
{
ReadOnlySpan<char> span = txt.AsSpan();
int firstCharPos = span.IndexOf(openChar);
int lastCharPos = -1;
if (firstCharPos != -1)
{
for (int n = firstCharPos + 1; n < span.Length; n++)
{
if (span[n] == openChar) firstCharPos = n; //This allows the opening char position to change
if (span[n] == closeChar) lastCharPos = n;
if (lastCharPos > firstCharPos) break;
//This would correctly extract "sales" from this [contrived]
//example: "just (a (name (sales) )))(test"
}
return span.Slice(firstCharPos + 1, lastCharPos - firstCharPos - 1).ToString();
}
return "";
}
Much similar to #Gustavo Baiocchi Costa but offset is being calculated with another intermediate Substring.
int innerTextStart = input.IndexOf("(") + 1;
int innerTextLength = input.Substring(start).IndexOf(")");
string output = input.Substring(innerTextStart, innerTextLength);
I came across this while I was looking for a solution to a very similar implementation.
Here is a snippet from my actual code. Starts substring from the first char (index 0).
string separator = "\n"; //line terminator
string output;
string input= "HowAreYou?\nLets go there!";
output = input.Substring(0, input.IndexOf(separator));

Categories