How to dynamically remove the last Char in a String in C# - c#

I am creating a console application upon which the user can type in a train station and find the train stations. For this, I am appending the Console.ReadKey().Key to a String each time.
When the user types an incorrect letter, I want the ConsoleKey.Backspace to remove the last Char in the String.
private void SetDepartingFrom()
{
String searchQuery = "";
ConsoleKey keyIn;
while ((keyIn = readKey(searchQuery)) != ConsoleKey.Enter)
{
if (keyIn == ConsoleKey.Backspace)
{
searchQuery.TrimEnd(searchQuery[searchQuery.Length - 1]);
}
else
{
searchQuery += keyIn.ToString();
}
}
}
private ConsoleKey readKey(String searchQuery)
{
Console.Clear();
Console.WriteLine("Stations Found:");
if (searchQuery != "")
App.Stations.FindAll(x => x.GetName().ToUpper().Contains(searchQuery.ToUpper())).ForEach(x => Console.WriteLine(x.GetName()));
else
Console.WriteLine("No Stations found...");
Console.Write("Search: " + searchQuery);
return Console.ReadKey().Key;
}
I have tried the following:
if (keyIn == ConsoleKey.Backspace)
searchQuery.TrimEnd(searchQuery[searchQuery.Length - 1]);
if (keyIn == ConsoleKey.Backspace)
searchQuery.Remove(searchQuery.Length -1);
if (keyIn == ConsoleKey.Backspace)
searchQuery[searchQuery.Length -1] = "";
None have worked. I understand Strings are immutable in C#, however, is this possible or is there a better way to achieve this?
Thanks in advance.

String is immutable so you have to use the value returned by TrimEnd.
searchQuery = searchQuery.TrimEnd(searchQuery[searchQuery.Length - 1]);
In this case I think Substring method would be more appropriate.

As you noted, strings are immutable. All of the instance methods on the string type (at least those related to "modifying" it) return a new string. This means that calling something like the following returns a new string which is immediately discarded:
// value is discarded
searchQuery.Substring(0, searchQuery.Length - 1);
The solution is to reassign the variable with the new value. For example:
searchQuery = searchQuery.Substring(0, searchQuery.Length - 1);
SharpLab example
If you are using C# 8 you can make use of the range operator via the Index/Range classes. This provides a bit cleaner version:
// backspace one character
searchQuery = searchQuery[..^1];
SharpLab example
I will also note that TrimEnd is most likely not what you want. It will trim more than one character at a time which isn't what a single press of the Backspace key would do. For example consider the following:
var str = "Abcdeee";
var result = str.TrimEnd('e');
Console.WriteLine(result); // prints "Abcd"
SharpLab example

Any method you use to manipulate the string will return the new string so you need to capture that.
string newString = searchQuery.Substring(0, searchQuery.Length -1);

It will return a new string, so you need to assign it to a string like this.
string newStr = earchQuery.Remove(searchQuery.Length -1);
Or to same string you can do like this.
earchQuery= earchQuery.Remove(searchQuery.Length -1);
You can also use TrimEnd and SubString methods.

You may try the following code example which removes the last character from a string.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
string founder = "Hell World from Big_Data_Analyst!";
string founderMinus1 = founder.Remove(founder.Length - 1, 1);
Console.WriteLine(founderMinus1);
}
}
}
The input string in the code is
Hell World from Big_Data_Analyst!
The output string is
Hell World from Big_Data_Analyst
As you see the last character which is ! is being removed in the output

Related

Equals() method not recognizing similar/same characters when comparing

Why comparing characters with .Equals always returns false?
char letter = 'a';
Console.WriteLine(letter.Equals("a")); // false
Overall I'm trying to write an English - Morse Code translator. I run into a problem comparing char values which shown above. I began with a foreach to analyze all the characters from a ReadLine() input, by using the WriteLine() method, all the characters were transposed fine, but when trying to compare them using the .Equals() method, no matter what I did, it always output false when trying to compare chars.
I have used the .Equals() method with other strings successfully, but it seems to not work with my chars.
using System;
public class MorseCode {
public static void Main (string[] args) {
Console.WriteLine ("Hello, write anything to convert it to morse code!");
var input = Console.ReadLine();
foreach (char letter in input) {
if(letter.Equals("a")) {
Console.WriteLine("Its A - live");
}
Console.WriteLine(letter);
}
var morseTranslation = "";
foreach (char letter in input) {
if(letter.Equals("a")) {
morseTranslation += ". _ - ";
}
if(letter.Equals("b")) {
morseTranslation += "_ . . . - ";
}
if(letter.Equals("c")) {
morseTranslation += "_ . _ . - ";
}
...
}
}
Console.WriteLine("In morse code, " + input + " is '"morseTranslation + "'");
}
}
At the beginning, I wrote the foreach to test if it recognized and ran the correct output, but in the end, when I wrote "sample" into the ReadLine(), it gave me :
Hello, write anything to convert it to morse code!
sample
s
a
m
p
l
e
When you do this:
var c = 'x';
var isEqual = c.Equals("x");
the result (isEqual) will always be false because it's comparing a string to a char. This would return true:
var isEqual = c.Equals('x');
The difference is that "x" is a string literal and 'x' is a char literal.
Part of what makes this confusing is that when you use an object's Equals method, it allows you to compare any type to any other type. So you could do this:
var x = 0;
var y = "y";
var isEqual = x.Equals(y);
...and the compiler will allow it, even though the comparison between int and string won't work. It will give you this warning:
When comparing value types like int or char with other values of the same type, we usually use ==, like
if (someChar == someOtherChar)
Then if you tried to do this:
if(someChar == "a")
It wouldn't compile. It would tell you that you're comparing a char to a string, and then it's easier because instead of running the program and looking for the error it just won't compile at all and it will tell you exactly where the problem is.
Just for the fun of it, here's another implementation.
public static class MorseCodeConverter
{
private static readonly Dictionary<char, string> Codes
= CreateMorseCodeDictionary();
public static string Convert(string input)
{
var lowerCase = input.ToLower();
var result = new StringBuilder();
foreach (var character in input)
{
if (Codes.ContainsKey(character))
result.Append(Codes[character]);
}
return result.ToString();
}
static Dictionary<char, string> CreateMorseCodeDictionary()
{
var result = new Dictionary<char, string>();
result.Add('a', ". _ - ");
result.Add('b', "_ . . . - ");
// add all the rest
return result;
}
}
One difference is that it's a class by itself without the console app. Then you can use it in a console app. Read the input from the keyboard and then call
MorseCodeConverter.Convert(input);
to get the result, and then you can print it to the console.a
Putting all of the characters in a dictionary means that instead of repeating the if/then you can just check to see if each character is in the dictionary.
It's important to remember that whilst the char and string keywords look reminiscant of eachother when looking at printed values you should note that they are not accomodated for in exactly the same way.
When you check a string you can use:
string s = "A";
if(s.Equals("A"))
{
//Do Something
}
However, the above will not work with a char. The difference between chars (value types) and strings (reference types) on a surface level is the use of access - single quote (apostrophe) vs quote.
To compare a char you can do this:
char s = 'A';
if(s.Equals('A'))
{
//Do Something
}
On a point relevant to your specific case however, morse code will only requre you to use a single case alphabet and as such when you try to compare against 'A' and 'a' you can call input.ToLower() to reduce your var (string) to all lower case so you don't need to cater for both upper and lower case alphabets.
It's good that you're aware of string comparissons and are not using direct value comparisson as this:
if (letter == 'a')
{
Console.WriteLine("Its A - live");
}
Would've allowed you to compare the char but it's bad practice as it may lead to lazy comparisson of strings in the same way and this:
if (letter == "a")
{
Console.WriteLine("Its A - live");
}
Is a non-representitive method of comparison for the purpose of comparing strings as it evaluates the reference not the direct value, see here
For char comparison you have to use single quote ' character not " this.
By the way it writes sample in decending order beacuse in your first foreach loop you write all letters in new line. SO below code will work for you:
using System;
public class MorseCode {
public static void Main (string[] args) {
Console.WriteLine ("Hello, write anything to convert it to morse code!");
var input = Console.ReadLine();
/*foreach (char letter in input) {
if(letter.Equals("a")) {
Console.WriteLine("Its A - live");
}
Console.WriteLine(letter);
}*/
var morseTranslation = "";
foreach (char letter in input) {
if(letter.Equals('a')) {
morseTranslation += ". _ - ";
}
if(letter.Equals('b')) {
morseTranslation += "_ . . . - ";
}
if(letter.Equals('c')) {
morseTranslation += "_ . _ . - ";
}
...
}
}
Console.WriteLine("In morse code, " + input + " is '"morseTranslation + "'");
}
}
In C#, you can compare strings like integers, that is with == operator. Equals is a method inherited from the object class, and normally implementations would make some type checks. char letter is (obviously) a character, while "a" is a single lettered string.
That's why it returns false.
You could use if (letter.Equals('a')) { ... }, or simpler if (letter == 'a') { ... }
Even simpler than that would be switch (letter) { case 'a': ...; break; ... }.
Or something that is more elegant but maybe too advanced yet for a beginner, using LINQ:
var validCharacters = "ABCDE...";
var codes = new string[] {
".-", "-...", "-.-.", "-..", ".", ...
};
var codes = input.ToUpper() // make uppercase
.ToCharArray() // explode string into single characters
.Select(validCharaters.IndexOf) // foreach element (i. e. character), get the result of "validCharacters.IndexOf",
// which equals the index of the morse code in the array "codes"
.Where(i => i > -1) // only take the indexes of characters that were found in "validCharacters"
.Select(i => codes[i]); // retrieve the matching entry from "codes" by index
// "codes" is now an IEnumerable<string>, a structure saying
// "I am a list of strings over which you can iterate,
// and I know how to generate the elements as you request them."
// Now concatenate all single codes to one long result string
var result = string.Join(" ", codes);

Insert spaces into string using string.format

I've been using C# String.Format for formatting numbers before like this (in this example I simply want to insert a space):
String.Format("{0:### ###}", 123456);
output:
"123 456"
In this particular case, the number is a string. My first thought was to simply parse it to a number, but it makes no sense in the context, and there must be a prettier way.
Following does not work, as ## looks for numbers
String.Format("{0:### ###}", "123456");
output:
"123456"
What is the string equivalent to # when formatting? The awesomeness of String.Format is still fairly new to me.
You have to parse the string to a number first.
int number = int.Parse("123456");
String.Format("{0:### ###}", number);
of course you could also use string methods but that's not as reliable and less safe:
string strNumber = "123456";
String.Format("{0} {1}", strNumber.Remove(3), strNumber.Substring(3));
As Heinzi pointed out, you can not have format specifier for string arguments.
So, instead of String.Format, you may use following:
string myNum="123456";
myNum=myNum.Insert(3," ");
Not very beautiful, and the extra work might outweigh the gains, but if the input is a string on that format, you could do:
var str = "123456";
var result = String.Format("{0} {1}", str.Substring(0,3), str.Substring(3));
string is not a IFormattable
Console.WriteLine("123456" is IFormattable); // False
Console.WriteLine(21321 is IFormattable); // True
No point to supply a format if the argument is not IFormattable only way is to convert your string to int or long
We're doing string manipulation, so we could always use a regex.
Adapted slightly from here:
class MyClass
{
static void Main(string[] args)
{
string sInput, sRegex;
// The string to search.
sInput = "123456789";
// The regular expression.
sRegex = "[0-9][0-9][0-9]";
Regex r = new Regex(sRegex);
MyClass c = new MyClass();
// Assign the replace method to the MatchEvaluator delegate.
MatchEvaluator myEvaluator = new MatchEvaluator(c.ReplaceNums);
// Replace matched characters using the delegate method.
sInput = r.Replace(sInput, myEvaluator);
// Write out the modified string.
Console.WriteLine(sInput);
}
public string ReplaceNums(Match m)
// Replace each Regex match with match + " "
{
return m.ToString()+" ";
}
}
How's that?
It's been ages since I used C# and I can't test, but this may work as a one-liner which may be "neater" if you only need it once:
sInput = Regex("[0-9][0-9][0-9]").Replace(sInput,MatchEvaluator(Match m => m.ToString()+" "));
There is no way to do what you want unless you parse the string first.
Based on your comments, you only really need a simple formatting so you are better off just implementing a small helper method and thats it. (IMHO it's not really a good idea to parse the string if it isn't logically a number; you can't really be sure that in the future the input string might not be a number at all.
I'd go for something similar to:
public static string Group(this string s, int groupSize = 3, char groupSeparator = ' ')
{
var formattedIdentifierBuilder = new StringBuilder();
for (int i = 0; i < s.Length; i++)
{
if (i != 0 && (s.Length - i) % groupSize == 0)
{
formattedIdentifierBuilder.Append(groupSeparator);
}
formattedIdentifierBuilder.Append(s[i]);
}
return formattedIdentifierBuilder.ToString();
}
EDIT: Generalized to generic grouping size and group separator.
The problem is that # is a Digit placeholder and it is specific to numeric formatting only. Hence, you can't use this on strings.
Either parse the string to a numeric, so the formatting rules apply, or use other methods to split the string in two.
string.Format("{0:### ###}", int.Parse("123456"));

Replacing characters in a string with another string

So what I am trying to do is as follows :
example of a string is A4PC
I am trying to replace for example any occurance of "A" with "[A4]" so I would get and similar any occurance of "4" with "[A4]"
"[A4][A4]PC"
I tried doing a normal Replace on the string but found out I got
"[A[A4]]PC"
string badWordAllVariants =
restriction.Value.Replace("A", "[A4]").Replace("4", "[A4]")
since I have two A's in a row causing an issue.
So I was thinking it would be better rather than the replace on the string I need to do it on a character per character basis and then build up a string again.
Is there anyway in Linq or so to do something like this ?
You don't need any LINQ here - String.Replace works just fine:
string input = "AAPC";
string result = input.Replace("A", "[A4]"); // "[A4][A4]PC"
UPDATE: For your updated requirements I suggest to use regular expression replace
string input = "A4PC";
var result = Regex.Replace(input, "A|4", "[A4]"); // "[A4][A4]PC"
This works well for me:
string x = "AAPC";
string replace = x.Replace("A", "[A4]");
EDIT:
Based on the updated question, the issue is the second replacement. In order to replace multiple strings you will want to do this sequentially:
var original = "AAPC";
// add arbitrary room to allow for more new characters
StringBuilder resultString = new StringBuilder(original.Length + 10);
foreach (char currentChar in original.ToCharArray())
{
if (currentChar == 'A') resultString.Append("[A4]");
else if (currentChar == '4') resultString.Append("[A4]");
else resultString.Append(currentChar);
}
string result = resultString.ToString();
You can run this routine with any replacements you want to make (in this case the letters 'A' and '4' and it should work. If you would want to replace strings the code would be similar in structure but you would need to "look ahead" and probably use a for loop. Hopefully this helps!
By the way - you want to use a string builder here and not strings because strings are static which means space gets allocated every time you loop. (Not good!)
I think this should do the trick
string str = "AA4PC";
string result = Regex.Replace(str, #"(?<Before>[^A4]?)(?<Value>A|4)(?<After>[^A4]?)", (m) =>
{
string before = m.Groups["Before"].Value;
string after = m.Groups["After"].Value;
string value = m.Groups["Value"].Value;
if (before != "[" || after != "]")
{
return "[A4]";
}
return m.ToString();
});
It is going to replace A and 4 that hasn't been replaced yet for [A4].

How to use replace only the first occurence of www

In my code behind in C# I have the following code. How do I change the replace so that only
the first occurance of www is replaced?
For example if the User enters www.testwww.com then I should be saving it as testwww.com.
Currently as per the below code it saves as www.com (guess due to substr code).
Please help. Thanks in advance.
private string FilterUrl(string url)
{
string lowerCaseUrl = url.ToLower();
lowerCaseUrl = lowerCaseUrl.Replace("http://", string.Empty).Replace("https://", string.Empty).Replace("ftp://", string.Empty);
lowerCaseUrl = lowerCaseUrl.Replace("www.", string.Empty);
string lCaseUrl = url.Substring(url.Length - lowerCaseUrl.Length, lowerCaseUrl.Length);
return lCaseUrl;
}
As Ally suggested. You are much better off using System.Uri. This also replaces the leading www as you wish.
private string FilterUrl(string url)
{
Uri uri = new UriBuilder(url).Uri; // defaults to http:// if missing
return Regex.Replace(uri.Host, "^www.", "") + uri.PathAndQuery;
}
Edit: The trailing slash is because of the PathAndQuery property. If there was no path you are left with the slash only. Just add another regex replace or string replace. Here's the regex way.
return Regex.Replace(uri.Host, "^www.", "") + Regex.Replace(uri.PathAndQuery, "/$", "");
I would suggest using indexOf(string) to find the first occurrence.
Edit: okay someone beat me to it ;)
You could use IndexOf like Felipe suggested OR do it the low tech way..
lowerCaseUrl = lowerCaseUrl.Replace("http://", string.Empty).Replace("https://", string.Empty).Replace("ftp://", string.Empty).Replace("http://www.", string.Empty).Replace("https://www.", string.Empty)
Would be interested to know what you're trying to achieve.
Came up with a cool static method, also works for replacing the first x occurrences:
public static string ReplaceOnce(this string s, string replace, string with)
{
return s.ReplaceCount(replace, with);
}
public static string ReplaceCount(this string s, string replace, string with, int howManytimes = 1)
{
if (howManytimes < 0) throw InvalidOperationException("can not replace a string less than zero times");
int count = 0;
while (s.Contains(replace) && count < howManytimes)
{
int position = s.IndexOf(replace);
s = s.Remove(position, replace.Length);
s = s.Insert(position, with);
count++;
}
return s;
}
The ReplaceOnce isn't necessary, just a simplifier. Call it like this:
string url = "http://www.stackoverflow.com/questions/www/www";
var urlR1 - url.ReplaceOnce("www", "xxx");
// urlR1 = "http://xxx.stackoverflow.com/questions/www/www";
var urlR2 - url.ReplaceCount("www", "xxx", 2);
// urlR2 = "http://xxx.stackoverflow.com/questions/xxx/www";
NOTE: this is case-sensitive as it is written
The Replace method will change all content of the string. You have to locate the piece you want to remove using IndexOf method, and remove using Remove method of string. Try something like this:
//include the namespace
using System.Globalization;
private string FilterUrl(string url)
{
// ccreate a Comparer object.
CompareInfo myCompare = CultureInfo.InvariantCulture.CompareInfo;
// find the 'www.' on the url parameter ignoring the case.
int position = myCompare.IndexOf(url, "www.", CompareOptions.IgnoreCase);
// check if exists 'www.' on the string.
if (position > -1)
{
if (position > 0)
url = url.Remove(position - 1, 5);
else
url = url.Remove(position, 5);
}
//if you want to remove http://, https://, ftp://.. keep this line
url = url.Replace("http://", string.Empty).Replace("https://", string.Empty).Replace("ftp://", string.Empty);
return url;
}
Edits
There was a part in your code that is removing a piece of string. If you just want to remove the 'www.' and 'http://', 'https://', 'ftp://', take a look the this code.
This code also ignore the case when it compares the url parameter and what you have been findind, on case, 'www.'.

Replace method in C# not working for individual character?

I've been looking around and it seems like I'm using this properly, but the results are failing. I want to go through and get rid of any 0's and replace them with o's.
newString = strOld.Replace('0', 'o'); // doesn't work.
newString = strOld.Replace("0", "o"); // doesn't work either.
Am I doing something wrong?
I made this test, and it works fine:
class Program
{
static void Main(string[] args)
{
var newString = "M0000".Replace('0', 'o');
}
}
Try a small test case, similar to the one I created, and see what happens.
Turns out in order to use the replace method it had to go into the same string. So while this won't work:
String newString;
String oldString = "b00k";
newString = oldString.Replace('0', 'o');
This will work:
String newString = "b00k";
newString = newString.Replace('0', 'o');
Appreciate all the feedback.
I was facing the same problem, actually just for information, I was doing something thing like myOldString.Replace("#", "No.");.
It was not working, I checked it.
Finally I found the solution, when i replaced the above string as
myOldString = myOldString.Replace("#","No");
string.replace makes a replica, earlier i was not assigning that to the actual string.
Make sure you are not making such t
Works fine here... the char version should work regardless of case if they were letters (I assume you're trying to replace zeroes with an lowercase O). Are you maybe using a font that does not distinguish between zero and the letter O or something like that?
You're not doing anything wrong. If you want to do a character replace instead of a string replace you need to do: s.Replace(char.Parse("0"), char.Parse("o")), but I can't think of any reason your code doesn't work.
The following link shows this very clearly: http://www.dotnetperls.com/replace
If you use string.Replace, it has to be assigned (as mentioned above by Geeklat):
String newString = "b00k";
newString = newString.Replace('0', 'o');
If you use "StringBuilder" the variable not have to be assigned -
Here is a Sample Program (Output is below):
using System;
using System.Text;
class Program
{
static void Main()
{
const string s = "This is an example.";
// A
// Create new StringBuilder from string
StringBuilder b = new StringBuilder(s);
Console.WriteLine(b);
// B
// Replace the first word
// The result doesn't need assignment
b.Replace("This", "Here");
Console.WriteLine(b);
// C
// Insert the string at the beginning
b.Insert(0, "Sentence: ");
Console.WriteLine(b);
}
}
Output:
This is an example.
Here is an example.
Sentence: Here is an example.

Categories

Resources