StringBuilder - find last index of a character - c#

I'd like to find a specific last character in a StringBuilder.
I know, I can solve it with while() but is there an build it option to do that easily?
eg:
private static StringBuilder mySb = new StringBuilder("");
mySb.Add("This is a test[n] I like Orange juice[n] Can you give me some?");
Now: It shoud find the ] and give me the possition. Like: 40
Thanks in advance

Since there is no builtin method and always creating a string from the StringBuilder via ToString can be quite inefficient you could create an extension method for this purpose:
public static int LastIndexOf(this StringBuilder sb, char find, bool ignoreCase = false, int startIndex = -1, CultureInfo culture = null)
{
if (sb == null) throw new ArgumentNullException(nameof(sb));
if (startIndex == -1) startIndex = sb.Length - 1;
if (startIndex < 0 || startIndex >= sb.Length) throw new ArgumentException("startIndex must be between 0 and sb.Lengh-1", nameof(sb));
if (culture == null) culture = CultureInfo.InvariantCulture;
int lastIndex = -1;
if (ignoreCase) find = Char.ToUpper(find, culture);
for (int i = startIndex; i >= 0; i--)
{
char c = ignoreCase ? Char.ToUpper(sb[i], culture) : (sb[i]);
if (find == c)
{
lastIndex = i;
break;
}
}
return lastIndex;
}
Add it to a static, accessible (extension) class, then you can use it in this way:
StringBuilder mySb = new StringBuilder("");
mySb.Append("This is a test[n] I like Orange juice[n] Can you give me some?");
int lastIndex = mySb.LastIndexOf(']'); // 39

Convert the StringBuilder to a string with the toString method, thereafter you can make use of the LastIndexOf method.
mySb.ToString().LastIndexOf(']');
LastIndexOf:
Reports the zero-based index position of the last occurrence of a
specified Unicode character or string within this instance. The method
returns -1 if the character or string is not found in this instance.
This member is overloaded. For complete information about this member,
including syntax, usage, and examples, click a name in the overload
list.

Related

Is there any way to compare strings with a startIndex?

I was just wondering if there is a way of comparing c# strings with a startIndex for the first one. I have a long string and an index and want to find if the next characters are equal to a string. The only way I have found to do this is:
public static bool Equals (string longString, int index, string compare) => longString.Substring(index, compare.Length) == compare;
I think this is inefficient because you are unnecessarily creating a substring for each time you compare a new string at the index. Is there a more efficient way? I looked at string.Equals but it doesn't look like something I would be comfortable trying to rewrite.
string.Compare(longString, startIndex, compare, 0, compare.Length, isIgnoreCase) == 0;
Please see:
https://learn.microsoft.com/en-us/dotnet/api/system.string.compare
This is the best solution I have found.
public static bool Equals (string longString, int index, string compare)
{
if (longString.Length > (index + compare.Length))
return false;
for (int n = 0; n < compare.Length; n++)
{
if (longString[index + n] != compare[n])
return false;
}
return true;
}
IndexOf() contains an overload where we can set startIndex and check whether the string contains desired word, then we should check if the sub-string after the startIndex has the same length as the desired word and note that the following solution doesn't create a new sub-string at all:
var startIndex = 2;
var longString = "56789";
var wordToFind = "789";
var isEqual = longString.IndexOf(wordToFind, startIndex) != -1 && wordToFind.Length == (longString.Length - startIndex);
Console.WriteLine(isEqual);

How to append every X in c# stringbuilder

I'm using unity and c# and am not sure how to use stringbuilder to append a "/" every X characters. I have code and can build a string from a array with the code below, and add a comma after each string, but i need to add the "/" after every x string
it should for example convert "122342" to "1,2,2,/,3,4,2". Currently it would convert it to "1,2,2,3,4,2"
this is the code i have already
StringBuilder Builtstring = new StringBuilder();
foreach(string griditem in tobuild){
Builtstring.Append(griditem).Append(",");
}
built = Builtstring.ToString();
Use a FOR loop and then check if the character is a factor of some desired nTH character. If so add an extra '/'.
int x = 2; // your x
StringBuilder builtstring = new StringBuilder();
for (int i = 0; i < tobuild.Length; i++)
{
string item = tobuild[i];
builtstring.Append(item).Append(",");
if (i%x==0) { builtstring.Append("/"); }
}
string built = builtstring.ToString();
Add an if statement to evaluate the character and then act accordingly.
StringBuilder Builtstring = new StringBuilder();
foreach(string griditem in tobuild){
if(griditem == 'x') { Builtstring.Append(griditem).Append(#"/"); }
Builtstring.Append(griditem).Append(",");
}
built = Builtstring.ToString();
Or if you actually want to count a certain number of characters before putting a slash you can do this.
int count = 10;
int position = 0;
StringBuilder Builtstring = new StringBuilder();
foreach(string griditem in tobuild){
if(position == count) { Builtstring.Append(griditem).Append(#"/"); position = 0; }
else{Builtstring.Append(griditem).Append(","); position++;}}
built = Builtstring.ToString();
You can iterate over the array of strings using a for loop, which provides an index.
For each iteration, add the current String to the StringBuilder, in addition to a ',' in case we still didn't reach the last string in the array.
Also, after x strings add a '/'. We can know that we reached x strings using the % (modulus) operator.
Notice that I start the loop from index = 1. I do that because the modulus operator for the value 0 with any positive number will yield 0, which will add the '/' char after the first word, something that we don't necessarily want.
static void Insert(StringBuilder b, int x, string[] tobuild)
{
for(var index = 1; index < tobuild.Length; ++index)
{
b.Append(tobuild[index]);
if(index != tobuild.Length -1)
{
b.Append(",");
}
if(0 == index % x)
{
b.Append("/");
}
}
}

Extracting from string

I have a quite complicated string that I need to extract from.
String - !0A!0B!0C #0A#0B#0B
I would like to read for each ! and extract them as a set of 3 meaning !0A , !0B , !0C and then I'm planning to use select case to read them as each of them represent something (Will select case be good?)
And how do I extract them in 3s
I would use regular expression. For example like this:
Dim str As String = "!0A!0B!0C"
Dim ptr As String = "[!][A-Z0-9]{2}"
Dim matches As MatchCollection = Regex.Matches(str, ptr)
For Each m In matches
Console.WriteLine(m.ToString())
Next
Sorry that the example is in VB but you get the idea from that.
This method should do the trick. Send your string in, and a list of results will come out:
public List<string> StringParser( string s ){
var list = new List<string>();
for( int i = 0; i < s.Length; i++ ){
if( s[i] == '!' ){
list.Add(s.Substring(i,3));
i+= 2;
}
}
return list;
}
Note that this will fail if the string contains a ! without at least two other characters after it, so you should probably perform a test to verify this before actually running the substring command.
If you have to do that in many places in your code you can create a fancy extension method:
static class StringExtensions {
public static IEnumerable<String> SplitInParts(this String s, Int32 partLength) {
if (s == null)
throw new ArgumentNullException("s");
if (partLength <= 0)
throw new ArgumentException("Part length has to be positive.", "partLength");
for (var i = 0; i < s.Length; i += partLength)
yield return s.Substring(i, Math.Min(partLength, s.Length - i));
}
}
You can then use it like this:
var parts = "!0A!0B!0C #0A#0B#0B".SplitInParts(3);

Format string with dashes

I have a compressed string value I'm extracting from an import file. I need to format this into a parcel number, which is formatted as follows: ##-##-##-###-###. So therefore, the string "410151000640" should become "41-01-51-000-640". I can do this with the following code:
String.Format("{0:##-##-##-###-###}", Convert.ToInt64("410151000640"));
However, The string may not be all numbers; it could have a letter or two in there, and thus the conversion to the int will fail. Is there a way to do this on a string so every character, regardless of if it is a number or letter, will fit into the format correctly?
Regex.Replace("410151000640", #"^(.{2})(.{2})(.{2})(.{3})(.{3})$", "$1-$2-$3-$4-$5");
Or the slightly shorter version
Regex.Replace("410151000640", #"^(..)(..)(..)(...)(...)$", "$1-$2-$3-$4-$5");
I would approach this by having your own formatting method, as long as you know that the "Parcel Number" always conforms to a specific rule.
public static string FormatParcelNumber(string input)
{
if(input.length != 12)
throw new FormatException("Invalid parcel number. Must be 12 characters");
return String.Format("{0}-{1}-{2}-{3}-{4}",
input.Substring(0,2),
input.Substring(2,2),
input.Substring(4,2),
input.Substring(6,3),
input.Substring(9,3));
}
This should work in your case:
string value = "410151000640";
for( int i = 2; i < value.Length; i+=3){
value = value.Insert( i, "-");
}
Now value contains the string with dashes inserted.
EDIT
I just now saw that you didn't have dashes between every second number all the way, to this will require a small tweak (and makes it a bit more clumsy also I'm afraid)
string value = "410151000640";
for( int i = 2; i < value.Length-1; i+=3){
if( value.Count( c => c == '-') >= 3) i++;
value = value.Insert( i, "-");
}
If its part of UI you can use MaskedTextProvider in System.ComponentModel
MaskedTextProvider prov = new MaskedTextProvider("aa-aa-aa-aaa-aaa");
prov.Set("41x151000a40");
string result = prov.ToDisplayString();
Here is a simple extension method with some utility:
public static string WithMask(this string s, string mask)
{
var slen = Math.Min(s.Length, mask.Length);
var charArray = new char[mask.Length];
var sPos = s.Length - 1;
for (var i = mask.Length - 1; i >= 0 && sPos >= 0;)
if (mask[i] == '#') charArray[i--] = s[sPos--];
else
charArray[i] = mask[i--];
return new string(charArray);
}
Use it as follows:
var s = "276000017812008";
var mask = "###-##-##-##-###-###";
var dashedS = s.WithMask(mask);
You can use it with any string and any character other than # in the mask will be inserted. The mask will work from right to left. You can tweak it to go the other way if you want.
Have fun.
If i understodd you correctly youre looking for a function that removes all letters from a string, aren't you?
I have created this on the fly, maybe you can convert it into c# if it's what you're looking for:
Dim str As String = "410151000vb640"
str = String.Format("{0:##-##-##-###-###}", Convert.ToInt64(MakeNumber(str)))
Public Function MakeNumber(ByVal stringInt As String) As String
Dim sb As New System.Text.StringBuilder
For i As Int32 = 0 To stringInt.Length - 1
If Char.IsDigit(stringInt(i)) Then
sb.Append(stringInt(i))
End If
Next
Return sb.ToString
End Function

c# getting a string within another string

i have a string like this:
some_string = "A simple demo of SMS text messaging.\r\n+CMGW: 3216\r\n\r\nOK\r\n\"
im coming from vb.net and i need to know in c#, if i know the position of CMGW, how do i get "3216" out of there?
i know that my start should be the position of CMGW + 6, but how do i make it stop as soon as it finds "\r" ??
again, my end result should be 3216
thank you!
Find the index of \r from the start of where you're interested in, and use the Substring overload which takes a length:
// Production code: add validation here.
// (Check for each index being -1, meaning "not found")
int cmgwIndex = text.IndexOf("CMGW: ");
// Just a helper variable; makes the code below slightly prettier
int startIndex = cmgwIndex + 6;
int crIndex = text.IndexOf("\r", startIndex);
string middlePart = text.Substring(startIndex, crIndex - startIndex);
If you know the position of 3216 then you can just do the following
string inner = some_string.SubString(positionOfCmgw+6,4);
This code will take the substring of some_string starting at the given position and only taking 4 characters.
If you want to be more general you could do the following
int start = positionOfCmgw+6;
int endIndex = some_string.IndexOf('\r', start);
int length = endIndex - start;
string inner = some_string.SubString(start, length);
One option would be to start from your known index and read characters until you hit a non-numeric value. Not the most robust solution, but it will work if you know your input's always going to look like this (i.e., no decimal points or other non-numeric characters within the numeric part of the string).
Something like this:
public static int GetNumberAtIndex(this string text, int index)
{
if (index < 0 || index >= text.Length)
throw new ArgumentOutOfRangeException("index");
var sb = new StringBuilder();
for (int i = index; i < text.Length; ++i)
{
char c = text[i];
if (!char.IsDigit(c))
break;
sb.Append(c);
}
if (sb.Length > 0)
return int.Parse(sb.ToString());
else
throw new ArgumentException("Unable to read number at the specified index.");
}
Usage in your case would look like:
string some_string = #"A simple demo of SMS text messaging.\r\n+CMGW: 3216\r\n...";
int index = some_string.IndexOf("CMGW") + 6;
int value = some_string.GetNumberAtIndex(index);
Console.WriteLine(value);
Output:
3216
If you're looking to extract the number portion of 'CMGW: 3216' then a more reliable method would be to use regular expressions. That way you can look for the entire pattern, and not just the header.
var some_string = "A simple demo of SMS text messaging.\r\n+CMGW: 3216\r\n\r\nOK\r\n";
var match = Regex.Match(some_string, #"CMGW\: (?<number>[0-9]+)", RegexOptions.Multiline);
var number = match.Groups["number"].Value;
More general, if you don't know the start position of CMGW but the structure remains as before.
String s;
char[] separators = {'\r'};
var parts = s.Split(separators);
parts.Where(part => part.Contains("CMGW")).Single().Reverse().TakeWhile(c => c != ' ').Reverse();

Categories

Resources