How to Trim exactly 1 whitespace after splitting the string - c#

I have made a program that evaluates a string by splitting it at a pipeline, the string are randomly generated and sometimes whitespace is a part of what need to be evaluated.
HftiVfzRIDBeotsnU uabjvLPC | LstHCfuobtv eVzDUBPn jIRfai
This string is same length on either side(2 x whitespace on left side of pipeline), but my problem comes when i have to trim the space on both sides of the pipeline (i do this after splitting)
is there some way of making sure that i only trim 1 single space instead of them all.
my code so far:
foreach (string s in str)
{
int bugCount = 0;
string[] info = s.Split('|');
string testCase = info[0].TrimEnd();
char[] testArr = testCase.ToCharArray();
string debugInfo = info[1].TrimStart();
char[] debugArr = debugInfo.ToCharArray();
int arrBound = debugArr.Count();
for (int i = 0; i < arrBound; i++)
if (testArr[i] != debugArr[i])
bugCount++;
if (bugCount <= 2 && bugCount != 0)
Console.WriteLine("Low");
if (bugCount <= 4 && bugCount != 0)
Console.WriteLine("Medium");
if (bugCount <= 6 && bugCount != 0)
Console.WriteLine("High");
if (bugCount > 6)
Console.WriteLine("Critical");
else
Console.WriteLine("Done");
}
Console.ReadLine();

You have 2 options.
If there is always 1 space before and after the pipe, split on {space}|{space}.
myInput.Split(new[]{" | "},StringSplitOptions.None);
Otherwise, instead of using TrimStart() & TrimEnd() use SubString.
var split = myInput.Split('|');
var s1 = split[0].EndsWith(" ")
? split[0].SubString(0,split[0].Length-1)
: split[0];
var s2 = split[1].StartsWith(" ")
? split[1].SubString(1) // to end of line
: split[1];
Note, there is some complexity here - if the pipe has no space around it, but the last/first character is a legitimate (data) space character the above will cut it off. You need more logic, but hopefully this will get you started!

There is no way to tell the Trim.. methods family to stop after cutting out some number of characters.
In general case, you'd need to do it manually by inspecting the parts obtained after Split and checking their first/last characters and substring'ing to get the correct part.
However, in your case, there's a much simpler way - the Split can also take a string as an argument, and even more - a set of strings:
string[] info = s.Split(new []{ " | " });
// or even
string[] info = s.Split(new []{ " | ", " |", "| ", "|" });
That should take care of the single spaces around the pipe | character by simply treating them as a part of the separator.

This is a string extension to trim space for count times, just in case.
public static class StringExtension
{
/// <summary>
/// Trim space at the end of string for count times
/// </summary>
/// <param name="input"></param>
/// <param name="count">number of space at the end to trim</param>
/// <returns></returns>
public static string TrimEnd(this string input, int count = 1)
{
string result = input;
if (count <= 0)
{
return result;
}
if (result.EndsWith(new string(' ', count)))
{
result = result.Substring(0, result.Length - count);
}
return result;
}
/// <summary>
/// Trim space at the start of string for count times
/// </summary>
/// <param name="input"></param>
/// <param name="count">number of space at the start to trim</param>
/// <returns></returns>
public static string TrimStart(this string input, int count = 1)
{
string result = input;
if (count <= 0)
{
return result;
}
if (result.StartsWith(new string(' ', count)))
{
result = result.Substring(count);
}
return result;
}
}
In the main
static void Main(string[] args)
{
string a = "1234 ";
string a1 = a.TrimEnd(1); // returns "1234 "
string a2 = a.TrimEnd(2); // returns "1234"
string a3 = a.TrimEnd(3); // returns "1234 "
string b = " 5678";
string b1 = b.TrimStart(1); // returns " 5678"
string b2 = b.TrimStart(2); // returns "5678"
string b3 = b.TrimStart(3); // returns " 5678"
}

Related

Efficient way to extract just one element from a delimited string

NOTE: I did ask the same question here but since some people have marked it as duplicate though it had some crafty, neat solutions, I had to create this extra(dupe) question to make it easier for others who are facing similar doubts. Added the question based on the suggestion of fellow stack overflow members.
What is the efficient way to parse through a large delimited string so that I can access just one element from the delimited set without having to store the other substrings involved?
I specifically am not interested in storing the rest of the element values as done when using Split() method since all of this information is irrelevant to the problem at hand. Also, I want to save memory in doing the same.
Problem Statement:
Given the exact delimited position, I need to extract the element contained in that given position in the most efficient way in terms of memory consumed and time taken.
Simple example string: "1,2,3,4,....,21,22,23,24"
Delimter: ,
Delimited Position: 22
Answer expected: 23
Another example string:
"61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;;""0x8D15A2913C934DE"";Thursday, 19-Jun-14 22:58:10 GMT;"
Delimiter: ;
Delimited Position: 7
Expected Answer: 23
There are some useful remarks relevant to this problem in the documentation for String.Split, although I wrote the following before discovering that.
One way to do it is to find a delimiter with String.IndexOf method - you can specify the index to start the search from, so it is possible to skip along the items without having to examine every character. (The examination of every character happens behind the scenes, but it's a little bit faster than doing it yourself.)
I made up an extension method by adding a new class named "ExtensionMethods.cs" to the solution with this content:
namespace ExtensionMethods
{
public static class MyExtensions
{
/// <summary>
/// Get the nth item from a delimited string.
/// </summary>
/// <param name="s">The string to retrieve a delimited item from.</param>
/// <param name="delimiter">The character used as the item delimiter.</param>
/// <param name="n">Zero-based index of item to return.</param>
/// <returns>The nth item or an empty string.</returns>
public static string Split(this string s, char delimiter, int n)
{
int pos = pos = s.IndexOf(delimiter);
if (n == 0 || pos < 0)
{ return (pos >= 0) ? s.Substring(0, pos) : s; }
int nDelims = 1;
while (nDelims < n && pos >= 0)
{
pos = s.IndexOf(delimiter, pos + 1);
nDelims++;
}
string result = "";
if (pos >= 0)
{
int nextDelim = s.IndexOf(delimiter, pos + 1);
result = (nextDelim < 0) ? s.Substring(pos + 1) : s.Substring(pos + 1, nextDelim - pos - 1);
}
return result;
}
}
}
And a small program to test it:
using System;
using System.Diagnostics;
using System.Linq;
using ExtensionMethods;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// test data...
string s = string.Join(";", Enumerable.Range(65, 26).Select(c => (char)c));
s = s.Insert(3, ";;;");
string o = "";
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 1; i <= 1000000; i++) {
o = s.Split(';', 21);
}
sw.Stop();
Console.WriteLine("Item directly selected: " + sw.ElapsedMilliseconds);
sw.Restart();
for (int i = 1; i <= 1000000; i++) {
o = s.Split(';')[21];
}
sw.Stop();
Console.WriteLine("Item from split array: " + sw.ElapsedMilliseconds + "\r\n");
Console.WriteLine(s);
Console.WriteLine(o);
Console.ReadLine();
}
}
}
Sample output:
Item directly selected: 1016
Item from split array: 1345
A;B;;;;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z
S
Reference: How to: Implement and Call a Custom Extension Method (C# Programming Guide)
try this:
public static string MyExtension(this string s, char delimiter, int n)
{
var begin = n== 0 ? 0 : Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n);
if (begin == -1)
return null;
var end = s.IndexOf(delimiter, begin + (n==0?0:1));
if (end == -1 ) end = s.Length;
//var end = Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n + 1);
var result = s.Substring(begin +1, end - begin -1 );
return result;
}
PS: Library used is Westwind.Utilities
Benchmark Code:
void Main()
{
string s = string.Join(";", Enumerable.Range(65, 26).Select(c => (char)c));
s = s.Insert(3, ";;;");
string o = "";
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 1; i <= 1000000; i++) {
o = s.Split(';', 21);
}
sw.Stop();
Console.WriteLine("Item directly selected: " + sw.ElapsedMilliseconds);
sw.Restart();
for (int i = 1; i <= 1000000; i++) {
o = s.MyExtension(';', 21);
}
sw.Stop();
Console.WriteLine("Item directly selected by MyExtension: " + sw.ElapsedMilliseconds);
sw.Restart();
for (int i = 1; i <= 1000000; i++) {
o = s.Split(';')[21];
}
sw.Stop();
Console.WriteLine("Item from split array: " + sw.ElapsedMilliseconds + "\r\n");
Console.WriteLine(s);
Console.WriteLine(o);
}
public static class MyExtensions
{
/// <summary>
/// Get the nth item from a delimited string.
/// </summary>
/// <param name="s">The string to retrieve a delimited item from.</param>
/// <param name="delimiter">The character used as the item delimiter.</param>
/// <param name="n">Zero-based index of item to return.</param>
/// <returns>The nth item or an empty string.</returns>
public static string Split(this string s, char delimiter, int n)
{
int pos = pos = s.IndexOf(delimiter);
if (n == 0 || pos < 0)
{ return (pos >= 0) ? s.Substring(0, pos) : s; }
int nDelims = 1;
while (nDelims < n && pos >= 0)
{
pos = s.IndexOf(delimiter, pos + 1);
nDelims++;
}
string result = "";
if (pos >= 0)
{
int nextDelim = s.IndexOf(delimiter, pos + 1);
result = (nextDelim < 0) ? s.Substring(pos + 1) : s.Substring(pos + 1, nextDelim - pos - 1);
}
return result;
}
public static string MyExtension(this string s, char delimiter, int n)
{
var begin = n== 0 ? 0 : Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n);
if (begin == -1)
return null;
var end = s.IndexOf(delimiter, begin + (n==0?0:1));
if (end == -1 ) end = s.Length;
//var end = Westwind.Utilities.StringUtils.IndexOfNth(s, delimiter, n + 1);
var result = s.Substring(begin +1, end - begin -1 );
return result;
}
}
Results:
Item directly selected: 277
Item directly selected by MyExtension: 114
Item from split array: 1297
A;B;;;;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z
S
Edit: Thanks to #Kalten, I enhanced solution further. Considerable difference has been seen on benchmark results.
By using the following Regex : ^([^;]*;){21}(.*?); , with that you don't have to generate the hole split list to search for your desired position, and once you reach it, it gonna be a matter of whether exists or not.
Explanation :
^ --> start of a line.
([^;]*;){Position - 1} --> notice that the symbol ; here is the delimiter, the expression will loop Pos - 1 times
(.*?) --> Non-Greedy .*
DEMO
For more about regular expressions on C# : documentation
In the example below i did implemant the two samples to show you how it works.
Match Method : documentation (Basically it searchs only for the first occurence of the pattern)
RegexOptions.Singleline : Treats the input as a signle line.
C# Code
Console.WriteLine("First Delimiter : ");
int Position = 22;
char delimiter = ',';
string pattern = #"^([^" + delimiter + "]*" + delimiter + "){" + (Position - 1) + #"}(.*?)" + delimiter;
Regex regex = new Regex(pattern, RegexOptions.Singleline);
// First Example
string Data = #"AAV,zzz,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22ABC,23,24,24";
Match Re = regex.Match(Data);
if (Re.Groups.Count > 0)
Console.WriteLine("\tMatch found : " + Re.Groups[2]);
// Second Example
Console.WriteLine("Second Delimiter : ");
Position = 8;
delimiter = ';';
pattern = #"^([^" + delimiter + "]*" + delimiter + "){" + (Position - 1) + #"}(.*?)" + delimiter;
Data = #"61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;;""0x8D15A2913C934DE"";Thursday, 19-Jun-14 22:58:10 GMT;";
regex = new Regex(pattern, RegexOptions.Singleline);
Re = regex.Match(Data);
if (Re.Groups.Count > 0)
Console.WriteLine("\tMatch found : " + Re.Groups[2]);
Output :
First Delimiter :
Match found : 22ABC
Second Delimiter :
Match found : 23
If you want to be sure the code parses the string in only one pass, and only parses what is needed, you can write the routine that iterates over the string yourself.
Since all c# strings implement IEnumerable<char> it is fairly straightforward to devise a method that requires zero string allocations:
static public IEnumerable<char> GetDelimitedField(this IEnumerable<char> source, char delimiter, int index)
{
foreach (var c in source)
{
if (c == delimiter)
{
if (--index < 0) yield break;
}
else
{
if (index == 0) yield return c;
}
}
}
This returns the result as an IEnumerable<char> but it's cheap to convert to a string. It's going to be a much shorter string at this point anyway.
static public string GetDelimitedString(this string source, char delimiter, int index)
{
var result = source.GetDelimitedField(delimiter, index);
return new string(result.ToArray());
}
And you can call it like this:
var input ="Zero,One,Two,Three,Four,Five,Six";
var output = input.GetDelimitedString(',',5);
Console.WriteLine(output);
Output:
Five
Example on DotNetFiddle
Too late for "answer" but this code gives me a run time of about 0.75 seconds with both strings processed 1,000,000 times. Difference this time is that now I'm not Marshaling an object but using pointers.
And this time I am returning a single new string (String.Substring).
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
string testString1 = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24";
string testString2 = "61d2e3f6-bcb7-4cd1-a81e-4f8f497f0da2;0;192.100.0.102:4362;2014-02-14;283;0;354;23;0;;;\"0x8D15A2913C934DE\";Thursday, 19-Jun-14 22:58:10 GMT;";
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 1; i < 1000000; i++)
{
Delimit(testString1, ',', 22);
Delimit(testString2, ';', 6);
}
sw.Stop();
Console.WriteLine($"==>{sw.ElapsedMilliseconds}");
Console.ReadLine();
}
static string Delimit(string stringUnderTest, char delimiter, int skipCount)
{
const int SIZE_OF_UNICHAR = 2;
int i = 0;
int index = 0;
char c = Char.MinValue;
GCHandle handle = GCHandle.Alloc(stringUnderTest, GCHandleType.Pinned);
try
{
IntPtr ptr = handle.AddrOfPinnedObject();
for (i = 0; i < skipCount; i++)
while ((char)Marshal.ReadByte(ptr, index += SIZE_OF_UNICHAR) != delimiter) ;
i = index;
while ((c = (char)Marshal.ReadByte(ptr, i += SIZE_OF_UNICHAR)) != delimiter) ;
}
finally
{
if (handle.IsAllocated)
handle.Free();
}
return stringUnderTest.Substring((index + SIZE_OF_UNICHAR) >> 1, (i - index - SIZE_OF_UNICHAR) >> 1);
}
}

Count words and spaces in string C#

I want to count words and spaces in my string. String looks like this:
Command do something ptuf(123) and bo(1).ctq[5] v:0,
I have something like this so far
int count = 0;
string mystring = "Command do something ptuf(123) and bo(1).ctq[5] v:0,";
foreach(char c in mystring)
{
if(char.IsLetter(c))
{
count++;
}
}
What should I do to count spaces also?
int countSpaces = mystring.Count(Char.IsWhiteSpace); // 6
int countWords = mystring.Split().Length; // 7
Note that both use Char.IsWhiteSpace which assumes other characters than " " as white-space(like newline). Have a look at the remarks section to see which exactly .
you can use string.Split with a space
http://msdn.microsoft.com/en-us/library/system.string.split.aspx
When you get a string array the number of elements is the number of words, and the number of spaces is the number of words -1
if you want to count spaces you can use LINQ :
int count = mystring.Count(s => s == ' ');
This will take into account:
Strings starting or ending with a space.
Double/triple/... spaces.
Assuming that the only word seperators are spaces and that your string is not null.
private static int CountWords(string S)
{
if (S.Length == 0)
return 0;
S = S.Trim();
while (S.Contains(" "))
S = S.Replace(" "," ");
return S.Split(' ').Length;
}
Note: the while loop can also be done with a regex: How do I replace multiple spaces with a single space in C#?
Here's a method using regex. Just something else to consider. It is better if you have long strings with lots of different types of whitespace. Similar to Microsoft Word's WordCount.
var str = "Command do something ptuf(123) and bo(1).ctq[5] v:0,";
int count = Regex.Matches(str, #"[\S]+").Count; // count is 7
For comparison,
var str = "Command do something ptuf(123) and bo(1).ctq[5] v:0,";
str.Count(char.IsWhiteSpace) is 17, while the regex count is still 7.
I've got some ready code to get a list of words in a string:
(extension methods, must be in a static class)
/// <summary>
/// Gets a list of words in the text. A word is any string sequence between two separators.
/// No word is added if separators are consecutive (would mean zero length words).
/// </summary>
public static List<string> GetWords(this string Text, char WordSeparator)
{
List<int> SeparatorIndices = Text.IndicesOf(WordSeparator.ToString(), true);
int LastIndexNext = 0;
List<string> Result = new List<string>();
foreach (int index in SeparatorIndices)
{
int WordLen = index - LastIndexNext;
if (WordLen > 0)
{
Result.Add(Text.Substring(LastIndexNext, WordLen));
}
LastIndexNext = index + 1;
}
return Result;
}
/// <summary>
/// returns all indices of the occurrences of a passed string in this string.
/// </summary>
public static List<int> IndicesOf(this string Text, string ToFind, bool IgnoreCase)
{
int Index = -1;
List<int> Result = new List<int>();
string T, F;
if (IgnoreCase)
{
T = Text.ToUpperInvariant();
F = ToFind.ToUpperInvariant();
}
else
{
T = Text;
F = ToFind;
}
do
{
Index = T.IndexOf(F, Index + 1);
Result.Add(Index);
}
while (Index != -1);
Result.RemoveAt(Result.Count - 1);
return Result;
}
/// <summary>
/// Implemented - returns all the strings in uppercase invariant.
/// </summary>
public static string[] ToUpperAll(this string[] Strings)
{
string[] Result = new string[Strings.Length];
Strings.ForEachIndex(i => Result[i] = Strings[i].ToUpperInvariant());
return Result;
}
In addition to Tim's entry, in case you have padding on either side, or multiple spaces beside each other:
Int32 words = somestring.Split( // your string
new[]{ ' ' }, // break apart by spaces
StringSplitOptions.RemoveEmptyEntries // remove empties (double spaces)
).Length; // number of "words" remaining
using namespace;
namespace Application;
class classname
{
static void Main(string[] args)
{
int count;
string name = "I am the student";
count = name.Split(' ').Length;
Console.WriteLine("The count is " +count);
Console.ReadLine();
}
}
if you need whitespace count only try this.
string myString="I Love Programming";
var strArray=myString.Split(new char[] { ' ' });
int countSpace=strArray.Length-1;
How about indirectly?
int countl = 0, countt = 0, count = 0;
foreach(char c in str)
{
countt++;
if (char.IsLetter(c))
{
countl++;
}
}
count = countt - countl;
Console.WriteLine("No. of spaces are: "+count);

Get string between two strings in a string

I have a string like:
"super example of string key : text I want to keep - end of my string"
I want to just keep the string which is between "key : " and " - ". How can I do that? Must I use a Regex or can I do it in another way?
Perhaps, a good way is just to cut out a substring:
String St = "super exemple of string key : text I want to keep - end of my string";
int pFrom = St.IndexOf("key : ") + "key : ".Length;
int pTo = St.LastIndexOf(" - ");
String result = St.Substring(pFrom, pTo - pFrom);
string input = "super exemple of string key : text I want to keep - end of my string";
var match = Regex.Match(input, #"key : (.+?)-").Groups[1].Value;
or with just string operations
var start = input.IndexOf("key : ") + 6;
var match2 = input.Substring(start, input.IndexOf("-") - start);
You can do it without regex
input.Split(new string[] {"key :"},StringSplitOptions.None)[1]
.Split('-')[0]
.Trim();
Here is the way how i can do that
public string Between(string STR , string FirstString, string LastString)
{
string FinalString;
int Pos1 = STR.IndexOf(FirstString) + FirstString.Length;
int Pos2 = STR.IndexOf(LastString);
FinalString = STR.Substring(Pos1, Pos2 - Pos1);
return FinalString;
}
Depending on how robust/flexible you want your implementation to be, this can actually be a bit tricky. Here's the implementation I use:
public static class StringExtensions {
/// <summary>
/// takes a substring between two anchor strings (or the end of the string if that anchor is null)
/// </summary>
/// <param name="this">a string</param>
/// <param name="from">an optional string to search after</param>
/// <param name="until">an optional string to search before</param>
/// <param name="comparison">an optional comparison for the search</param>
/// <returns>a substring based on the search</returns>
public static string Substring(this string #this, string from = null, string until = null, StringComparison comparison = StringComparison.InvariantCulture)
{
var fromLength = (from ?? string.Empty).Length;
var startIndex = !string.IsNullOrEmpty(from)
? #this.IndexOf(from, comparison) + fromLength
: 0;
if (startIndex < fromLength) { throw new ArgumentException("from: Failed to find an instance of the first anchor"); }
var endIndex = !string.IsNullOrEmpty(until)
? #this.IndexOf(until, startIndex, comparison)
: #this.Length;
if (endIndex < 0) { throw new ArgumentException("until: Failed to find an instance of the last anchor"); }
var subString = #this.Substring(startIndex, endIndex - startIndex);
return subString;
}
}
// usage:
var between = "a - to keep x more stuff".Substring(from: "-", until: "x");
// returns " to keep "
I think this works:
static void Main(string[] args)
{
String text = "One=1,Two=2,ThreeFour=34";
Console.WriteLine(betweenStrings(text, "One=", ",")); // 1
Console.WriteLine(betweenStrings(text, "Two=", ",")); // 2
Console.WriteLine(betweenStrings(text, "ThreeFour=", "")); // 34
Console.ReadKey();
}
public static String betweenStrings(String text, String start, String end)
{
int p1 = text.IndexOf(start) + start.Length;
int p2 = text.IndexOf(end, p1);
if (end == "") return (text.Substring(p1));
else return text.Substring(p1, p2 - p1);
}
Regex is overkill here.
You could use string.Split with the overload that takes a string[] for the delimiters but that would also be overkill.
Look at Substring and IndexOf - the former to get parts of a string given and index and a length and the second for finding indexed of inner strings/characters.
A working LINQ solution:
string str = "super example of string key : text I want to keep - end of my string";
string res = new string(str.SkipWhile(c => c != ':')
.Skip(1)
.TakeWhile(c => c != '-')
.ToArray()).Trim();
Console.WriteLine(res); // text I want to keep
In C# 8.0 and above, you can use the range operator .. as in
var s = "header-THE_TARGET_STRING.7z";
var from = s.IndexOf("-") + "-".Length;
var to = s.IndexOf(".7z");
var versionString = s[from..to]; // THE_TARGET_STRING
See documentation for details.
string str="super exemple of string key : text I want to keep - end of my string";
int startIndex = str.IndexOf("key") + "key".Length;
int endIndex = str.IndexOf("-");
string newString = str.Substring(startIndex, endIndex - startIndex);
or, with a regex.
using System.Text.RegularExpressions;
...
var value =
Regex.Match(
"super exemple of string key : text I want to keep - end of my string",
"key : (.*) - ")
.Groups[1].Value;
with a running example.
You can decide if its overkill.
or
as an under validated extension method
using System.Text.RegularExpressions;
public class Test
{
public static void Main()
{
var value =
"super exemple of string key : text I want to keep - end of my string"
.Between(
"key : ",
" - ");
Console.WriteLine(value);
}
}
public static class Ext
{
static string Between(this string source, string left, string right)
{
return Regex.Match(
source,
string.Format("{0}(.*){1}", left, right))
.Groups[1].Value;
}
}
Since the : and the - are unique you could use:
string input;
string output;
input = "super example of string key : text I want to keep - end of my string";
output = input.Split(new char[] { ':', '-' })[1];
I used the code snippet from Vijay Singh Rana which basically does the job. But it causes problems if the firstString does already contain the lastString. What I wanted was extracting a access_token from a JSON Response (no JSON Parser loaded). My firstString was \"access_token\": \" and my lastString was \". I ended up with a little modification
string Between(string str, string firstString, string lastString)
{
int pos1 = str.IndexOf(firstString) + firstString.Length;
int pos2 = str.Substring(pos1).IndexOf(lastString);
return str.Substring(pos1, pos2);
}
You can use the extension method below:
public static string GetStringBetween(this string token, string first, string second)
{
if (!token.Contains(first)) return "";
var afterFirst = token.Split(new[] { first }, StringSplitOptions.None)[1];
if (!afterFirst.Contains(second)) return "";
var result = afterFirst.Split(new[] { second }, StringSplitOptions.None)[0];
return result;
}
Usage is:
var token = "super exemple of string key : text I want to keep - end of my string";
var keyValue = token.GetStringBetween("key : ", " - ");
var matches = Regex.Matches(input, #"(?<=key :)(.+?)(?=-)");
This returns only the value(s) between "key :" and the following occurance of "-"
If you are looking for a 1 line solution, this is it:
s.Substring(s.IndexOf("eT") + "eT".Length).Split("97".ToCharArray()).First()
The whole 1 line solution, with System.Linq:
using System;
using System.Linq;
class OneLiner
{
static void Main()
{
string s = "TextHereTisImortant973End"; //Between "eT" and "97"
Console.WriteLine(s.Substring(s.IndexOf("eT") + "eT".Length)
.Split("97".ToCharArray()).First());
}
}
private string gettxtbettwen(string txt, string first, string last)
{
StringBuilder sb = new StringBuilder(txt);
int pos1 = txt.IndexOf(first) + first.Length;
int len = (txt.Length ) - pos1;
string reminder = txt.Substring(pos1, len);
int pos2 = reminder.IndexOf(last) - last.Length +1;
return reminder.Substring(0, pos2);
}
When questions are stated in terms of a single example ambiguities are inevitably present. This question is no exception.
For the example given in the question the desired string is clear:
super example of string key : text I want to keep - end of my string
^^^^^^^^^^^^^^^^^^^
However, this string is but an example of strings and boundary strings for which certain substrings are to be identified. I will consider a generic string with generic boundary strings, represented as follows.
abc FF def PP ghi,PP jkl,FF mno PP pqr FF,stu FF vwx,PP yza
^^^^^^^^^^^^ ^^^^^
PP is the preceding string, FF is the following string and the party hats indicate which substrings are to be matched. (In the example given in the question key : is the preceding string and - is the following string.) I have assumed that PP and FF are preceded and followed by word boundaries (so that PPA and FF8 are not matched).
My assumptions, as reflected by the party hats, are as follows:
The first substring PP may be preceded by one (or more) FF substrings, which, if present, are disregarded;
If PP is followed by one or more PPs before FF is encountered, the following PPs are part of the substring between the preceding and following strings;
If PP is followed by one or more FFs before a PP is encounter, the first FF following PP is considered to be the following string.
Note that many of the answers here deal with only strings of the form
abc PP def FF ghi
^^^^^
or
abc PP def FF ghi PP jkl FF mno
^^^^^ ^^^^^
One may use a regular expression, code constructs, or a combination of the two to identify the substrings of interest. I make no judgement as to which approach is best. I will only present the following regular expression that will match the substrings of interest.
(?<=\bPP\b)(?:(?!\bFF\b).)*(?=\bFF\b)
Start your engine!1
I tested this with the PCRE (PHP) regex engine, but as the regex is not at all exotic, I am sure it will work with the .NET regex engine (which is very robust).
The regex engine performs the following operations:
(?<= : begin a positive lookbehind
\bPP\b : match 'PP'
) : end positive lookbehind
(?: : begin a non-capture group
(?! : begin a negative lookahead
\bFF\b : match 'FF'
) : end negative lookahead
. : match any character
) : end non-capture group
* : execute non-capture group 0+ times
(?= : begin positive lookahead
\bFF\b : match 'FF'
) : end positive lookahead
This technique, of matching one character at a time, following the preceding string, until the character is F and is followed by F (or more generally, the character beings the string that constitutes the following string), is called Tempered Greedy Token Solution.
Naturally, the regex would have to be modified (if possible) if the assumptions I set out above are changed.
1. Move the cursor around for detailed explanations.
If you want to handle multiple occurrences of substring pairs, it won't be easy without RegEx:
Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
input ?? String.Empty avoids argument null exception
?= keeps 1st substring and ?<= keeps 2nd substring
RegexOptions.Singleline allows newline between substring pair
If order & occurrence count of substrings doesn't matter, this quick & dirty one may be an option:
var parts = input?.Split(new string[] { "key : ", " - " }, StringSplitOptions.None);
string result = parts?.Length >= 3 ? result[1] : input;
At least it avoids most exceptions, by returning the original string if none/single substring match.
For get string between string's, I'm using this method:
public static class Extension
{
/// <summary>
/// Gets currently string between
/// </summary>
/// <param name="word">Currently string</param>
/// <param name="start">String left</param>
/// <param name="end">String right</param>
/// <returns>String between start and end</returns>
/// <example>The string "value (4815162342)" use Between("(",")") generates in method: "4815162342"</example>
public static string Between(this string word, string start, string end)
{
if (start.Equals(end))
throw new ArgumentException("Start string can't equals a end string.");
int startIndex = word.LastIndexOf(start) + 1;
int endIndex = word.LastIndexOf(end) - 1 - word.LastIndexOf(start);
return word.Substring(startIndex, endIndex);
}
}
You already have some good answers and I realize the code I am providing is far from the most efficient and clean. However, I thought it might be useful for educational purposes. We can use pre-built classes and libraries all day long. But without understanding the inner-workings, we are simply mimicking and repeating and will never learn anything. This code works and is more basic or "virgin" than some of the others:
char startDelimiter = ':';
char endDelimiter = '-';
Boolean collect = false;
string parsedString = "";
foreach (char c in originalString)
{
if (c == startDelimiter)
collect = true;
if (c == endDelimiter)
collect = false;
if (collect == true && c != startDelimiter)
parsedString += c;
}
You end up with your desired string assigned to the parsedString variable. Keep in mind that it will also capture proceeding and preceding spaces. Remember that a string is simply an array of characters that can be manipulated like other arrays with indices etc.
Take care.
As I always say nothing is impossible:
string value = "super exemple of string key : text I want to keep - end of my string";
Regex regex = new Regex(#"(key \: (.*?) _ )");
Match match = regex.Match(value);
if (match.Success)
{
Messagebox.Show(match.Value);
}
Remeber that should add reference of System.Text.RegularExpressions
Hope That I Helped.
Something like this perhaps
private static string Between(string text, string from, string to)
{
return text[(text.IndexOf(from)+from.Length)..text.IndexOf(to, text.IndexOf(from))];
}
getStringBetween(startStr, endStr, fullStr) {
string startIndex = fullStr.indexOf(startStr);
string endIndex= fullStr.indexOf(endStr);
return fullStr.substring(startIndex + startStr.length, endIndex);
}
Here it is;
/// <summary>
///
/// </summary>
/// <param name="line"></param>
/// <param name="begin_tag"></param>
/// <param name="end_tag"></param>
/// <param name="lastIndexOfEndTag"></param>
/// <returns></returns>
private string getBetween(string line, string begin_tag, string end_tag, bool lastIndexOfEndTag = false, bool returnNullIfTagsNotExists = false)
{
if (!string.IsNullOrEmpty(line) && !string.IsNullOrEmpty(begin_tag) && !string.IsNullOrEmpty(end_tag))
{
// 1 2 3 4 5 6 7
//0123456789012345678901234567890123456789012345678901234567890123456789012
//StdErrorData: Duration: 01:59:54.88, start: 0.000000, bitrate: 557 kb/s
int startIndex = line.IndexOf(begin_tag);
if (startIndex >= 0)
{
startIndex += begin_tag.Length;
}
else
{
if (returnNullIfTagsNotExists)
{
return null;
}
else
{
startIndex = 0;
}
}
int endIndex = lastIndexOfEndTag ?
line.LastIndexOf(end_tag, startIndex)
: line.IndexOf(end_tag, startIndex);
if (endIndex > startIndex)
{
return line.Substring(startIndex, endIndex - startIndex);
}
else
{
if (returnNullIfTagsNotExists)
{
return null;
}
else
{
return line.Substring(startIndex);
}
}
}
return null;
}
Test;
string r = getBetween("StdErrorData: Duration: 01:59:54.88, start: 0.000000, bitrate: 557 kb/s", "Duration:", ",");
Console.WriteLine($"<{r}>");
//< 01:59:54.88>
Here is the extension method in case anyone interested in keeping the start and end text as well.
public static string SubstringBetween(this string text, string start, string end, bool keepStartEndText = false)
{
var startIndex = text.IndexOf(start);
var endIndex = text.LastIndexOf(end);
if (keepStartEndText)
return text.Substring(startIndex, (endIndex + end.Length) - startIndex);
else
return text.Substring(startIndex + start.Length, endIndex - (startIndex + start.Length));
}
public static string ExtractBetweenTwoStrings(string FullText, string StartString, string EndString, bool IncludeStartString, bool IncludeEndString)
{
try { int Pos1 = FullText.IndexOf(StartString) + StartString.Length; int Pos2 = FullText.IndexOf(EndString, Pos1); return ((IncludeStartString) ? StartString : "")
+ FullText.Substring(Pos1, Pos2 - Pos1) + ((IncludeEndString) ? EndString : ""); } catch (Exception ex) { return ex.ToString(); } //return ""; }
}
credit to: https://www.c-sharpcorner.com/blogs/how-to-extract-a-string-lies-between-two-strings-in-c-sharpnet1

multiline formatting for verbatim strings in c# (prefix with #)

I love using the #"strings" in c#, especially when I have a lot of multi-line text. The only annoyance is that my code formatting goes to doodie when doing this, because the second and greater lines are pushed fully to the left instead of using the indentation of my beautifully formatted code. I know this is by design, but is there some option/hack way of allowing these lines to be indented, without adding the actual tabs/spaces to the output?
adding example:
var MyString = #" this is
a multi-line string
in c#.";
My variable declaration is indented to the "correct" depth, but the second and further lines in the string get pushed to the left margin- so the code is kinda ugly. You could add tabs to the start of line 2 and 3, but the string itself would then contain those tabs... make sense?
How about a string extension? Update: I reread your question and I hope there is a better answer. This is something that bugs me too and having to solve it as below is frustrating but on the plus side it does work.
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
public static class StringExtensions
{
public static string StripLeadingWhitespace(this string s)
{
Regex r = new Regex(#"^\s+", RegexOptions.Multiline);
return r.Replace(s, string.Empty);
}
}
}
And an example console program:
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string x = #"This is a test
of the emergency
broadcasting system.";
Console.WriteLine(x);
Console.WriteLine();
Console.WriteLine("---");
Console.WriteLine();
Console.WriteLine(x.StripLeadingWhitespace());
Console.ReadKey();
}
}
}
And the output:
This is a test
of the emergency
broadcasting system.
---
This is a test
of the emergency
broadcasting system.
And a cleaner way to use it if you decide to go this route:
string x = #"This is a test
of the emergency
broadcasting system.".StripLeadingWhitespace();
// consider renaming extension to say TrimIndent() or similar if used this way
Cymen has given the right solution. I use a similar approach as derived from Scala's stripMargin() method. Here's what my extension method looks like:
public static string StripMargin(this string s)
{
return Regex.Replace(s, #"[ \t]+\|", string.Empty);
}
Usage:
var mystring = #"
|SELECT
| *
|FROM
| SomeTable
|WHERE
| SomeColumn IS NOT NULL"
.StripMargin();
Result:
SELECT
*
FROM
SomeTable
WHERE
SomeColumn IS NOT NULL
I can't think of an answer that would completely satisfy your question, however you could write a function that strips leading spaces from lines of text contained in a string and call it on each creation of such a string.
var myString = TrimLeadingSpacesOfLines(#" this is a
a multi-line string
in c#.");
Yes it is a hack, but you specified your acceptance of a hack in your question.
Here is a longish solution which tries to mimic textwrap.dedent as much as possible. The first line is left as-is and expected not to be indented. (You can generate the unit tests based on the doctests using doctest-csharp.)
/// <summary>
/// Imitates the Python's
/// <a href="https://docs.python.org/3/library/textwrap.html#textwrap.dedent">
/// <c>textwrap.dedent</c></a>.
/// </summary>
/// <param name="text">Text to be dedented</param>
/// <returns>array of dedented lines</returns>
/// <code doctest="true">
/// Assert.That(Dedent(""), Is.EquivalentTo(new[] {""}));
/// Assert.That(Dedent("test me"), Is.EquivalentTo(new[] {"test me"}));
/// Assert.That(Dedent("test\nme"), Is.EquivalentTo(new[] {"test", "me"}));
/// Assert.That(Dedent("test\n me"), Is.EquivalentTo(new[] {"test", " me"}));
/// Assert.That(Dedent("test\n me\n again"), Is.EquivalentTo(new[] {"test", "me", " again"}));
/// Assert.That(Dedent(" test\n me\n again"), Is.EquivalentTo(new[] {" test", "me", " again"}));
/// </code>
private static string[] Dedent(string text)
{
var lines = text.Split(
new[] {"\r\n", "\r", "\n"},
StringSplitOptions.None);
// Search for the first non-empty line starting from the second line.
// The first line is not expected to be indented.
var firstNonemptyLine = -1;
for (var i = 1; i < lines.Length; i++)
{
if (lines[i].Length == 0) continue;
firstNonemptyLine = i;
break;
}
if (firstNonemptyLine < 0) return lines;
// Search for the second non-empty line.
// If there is no second non-empty line, we can return immediately as we
// can not pin the indent.
var secondNonemptyLine = -1;
for (var i = firstNonemptyLine + 1; i < lines.Length; i++)
{
if (lines[i].Length == 0) continue;
secondNonemptyLine = i;
break;
}
if (secondNonemptyLine < 0) return lines;
// Match the common prefix with at least two non-empty lines
var firstNonemptyLineLength = lines[firstNonemptyLine].Length;
var prefixLength = 0;
for (int column = 0; column < firstNonemptyLineLength; column++)
{
char c = lines[firstNonemptyLine][column];
if (c != ' ' && c != '\t') break;
bool matched = true;
for (int lineIdx = firstNonemptyLine + 1; lineIdx < lines.Length;
lineIdx++)
{
if (lines[lineIdx].Length == 0) continue;
if (lines[lineIdx].Length < column + 1)
{
matched = false;
break;
}
if (lines[lineIdx][column] != c)
{
matched = false;
break;
}
}
if (!matched) break;
prefixLength++;
}
if (prefixLength == 0) return lines;
for (var i = 1; i < lines.Length; i++)
{
if (lines[i].Length > 0) lines[i] = lines[i].Substring(prefixLength);
}
return lines;
}

String.Replace ignoring case

I have a string called "hello world"
I need to replace the word "world" to "csharp"
for this I use:
string.Replace("World", "csharp");
but as a result, I don't get the string replaced. The reason is case sensitiveness. The original string contains "world" whereas I'm trying to replace "World".
Is there any way to avoid this case sensitiveness in string.Replace method?
You could use a Regex and perform a case insensitive replace:
class Program
{
static void Main()
{
string input = "hello WoRlD";
string result =
Regex.Replace(input, "world", "csharp", RegexOptions.IgnoreCase);
Console.WriteLine(result); // prints "hello csharp"
}
}
var search = "world";
var replacement = "csharp";
string result = Regex.Replace(
stringToLookInto,
Regex.Escape(search),
replacement.Replace("$","$$"),
RegexOptions.IgnoreCase
);
The Regex.Escape is useful if you rely on user input which can contains Regex language elements
Update
Thanks to comments, you actually don't have to escape the replacement string.
Here is a small fiddle that tests the code:
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
var tests = new[] {
new { Input="abcdef", Search="abc", Replacement="xyz", Expected="xyzdef" },
new { Input="ABCdef", Search="abc", Replacement="xyz", Expected="xyzdef" },
new { Input="A*BCdef", Search="a*bc", Replacement="xyz", Expected="xyzdef" },
new { Input="abcdef", Search="abc", Replacement="x*yz", Expected="x*yzdef" },
new { Input="abcdef", Search="abc", Replacement="$", Expected="$def" },
};
foreach(var test in tests){
var result = ReplaceCaseInsensitive(test.Input, test.Search, test.Replacement);
Console.WriteLine(
"Success: {0}, Actual: {1}, {2}",
result == test.Expected,
result,
test
);
}
}
private static string ReplaceCaseInsensitive(string input, string search, string replacement){
string result = Regex.Replace(
input,
Regex.Escape(search),
replacement.Replace("$","$$"),
RegexOptions.IgnoreCase
);
return result;
}
}
Its output is:
Success: True, Actual: xyzdef, { Input = abcdef, Search = abc, Replacement = xyz, Expected = xyzdef }
Success: True, Actual: xyzdef, { Input = ABCdef, Search = abc, Replacement = xyz, Expected = xyzdef }
Success: True, Actual: xyzdef, { Input = A*BCdef, Search = a*bc, Replacement = xyz, Expected = xyzdef }
Success: True, Actual: x*yzdef, { Input = abcdef, Search = abc, Replacement = x*yz, Expected = x*yzdef}
Success: True, Actual: $def, { Input = abcdef, Search = abc, Replacement = $, Expected = $def }
2.5X FASTER and MOST EFFECTIVE method than other's regular expressions methods:
/// <summary>
/// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another
/// specified string according the type of search to use for the specified string.
/// </summary>
/// <param name="str">The string performing the replace method.</param>
/// <param name="oldValue">The string to be replaced.</param>
/// <param name="newValue">The string replace all occurrences of <paramref name="oldValue"/>.
/// If value is equal to <c>null</c>, than all occurrences of <paramref name="oldValue"/> will be removed from the <paramref name="str"/>.</param>
/// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
/// <returns>A string that is equivalent to the current string except that all instances of <paramref name="oldValue"/> are replaced with <paramref name="newValue"/>.
/// If <paramref name="oldValue"/> is not found in the current instance, the method returns the current instance unchanged.</returns>
[DebuggerStepThrough]
public static string Replace(this string str,
string oldValue, string newValue,
StringComparison comparisonType)
{
// Check inputs.
if (str == null)
{
// Same as original .NET C# string.Replace behavior.
throw new ArgumentNullException(nameof(str));
}
if (str.Length == 0)
{
// Same as original .NET C# string.Replace behavior.
return str;
}
if (oldValue == null)
{
// Same as original .NET C# string.Replace behavior.
throw new ArgumentNullException(nameof(oldValue));
}
if (oldValue.Length == 0)
{
// Same as original .NET C# string.Replace behavior.
throw new ArgumentException("String cannot be of zero length.");
}
//if (oldValue.Equals(newValue, comparisonType))
//{
//This condition has no sense
//It will prevent method from replacesing: "Example", "ExAmPlE", "EXAMPLE" to "example"
//return str;
//}
// Prepare string builder for storing the processed string.
// Note: StringBuilder has a better performance than String by 30-40%.
StringBuilder resultStringBuilder = new StringBuilder(str.Length);
// Analyze the replacement: replace or remove.
bool isReplacementNullOrEmpty = string.IsNullOrEmpty(newValue);
// Replace all values.
const int valueNotFound = -1;
int foundAt;
int startSearchFromIndex = 0;
while ((foundAt = str.IndexOf(oldValue, startSearchFromIndex, comparisonType)) != valueNotFound)
{
// Append all characters until the found replacement.
int charsUntilReplacment = foundAt - startSearchFromIndex;
bool isNothingToAppend = charsUntilReplacment == 0;
if (!isNothingToAppend)
{
resultStringBuilder.Append(str, startSearchFromIndex, charsUntilReplacment);
}
// Process the replacement.
if (!isReplacementNullOrEmpty)
{
resultStringBuilder.Append(newValue);
}
// Prepare start index for the next search.
// This needed to prevent infinite loop, otherwise method always start search
// from the start of the string. For example: if an oldValue == "EXAMPLE", newValue == "example"
// and comparisonType == "any ignore case" will conquer to replacing:
// "EXAMPLE" to "example" to "example" to "example" … infinite loop.
startSearchFromIndex = foundAt + oldValue.Length;
if (startSearchFromIndex == str.Length)
{
// It is end of the input string: no more space for the next search.
// The input string ends with a value that has already been replaced.
// Therefore, the string builder with the result is complete and no further action is required.
return resultStringBuilder.ToString();
}
}
// Append the last part to the result.
int charsUntilStringEnd = str.Length - startSearchFromIndex;
resultStringBuilder.Append(str, startSearchFromIndex, charsUntilStringEnd);
return resultStringBuilder.ToString();
}
Note: ignore case == StringComparison.OrdinalIgnoreCase as parameter for StringComparison comparisonType. It is the fastest, case-insensitive way to replace all values.
Advantages of this method:
High CPU and MEMORY efficiency;
It is the fastest solution, 2.5 times faster than other's methods
with regular expressions (proof in the end);
Suitable for removing parts from the input string (set newValue to
null), optimized for this;
Same as original .NET C# string.Replace behavior, same exceptions;
Well commented, easy to understand;
Simpler – no regular expressions. Regular expressions are always slower because of their versatility (even compiled);
This method is well tested and there are no hidden flaws like infinite loop in other's solutions, even highly rated:
#AsValeO: Not works with Regex language elements, so it's not
universal method
#Mike Stillion: There is a problem with this code. If the text in new
is a superset of the text in old, this can produce an endless loop.
Benchmark-proof: this solution is 2.59X times faster than regex from #Steve B., code:
// Results:
// 1/2. Regular expression solution: 4486 milliseconds
// 2/2. Current solution: 1727 milliseconds — 2.59X times FASTER! than regex!
// Notes: the test was started 5 times, the result is an average; release build.
const int benchmarkIterations = 1000000;
const string sourceString = "aaaaddsdsdsdsdsd";
const string oldValue = "D";
const string newValue = "Fod";
long totalLenght = 0;
Stopwatch regexStopwatch = Stopwatch.StartNew();
string tempString1;
for (int i = 0; i < benchmarkIterations; i++)
{
tempString1 = sourceString;
tempString1 = ReplaceCaseInsensitive(tempString1, oldValue, newValue);
totalLenght = totalLenght + tempString1.Length;
}
regexStopwatch.Stop();
Stopwatch currentSolutionStopwatch = Stopwatch.StartNew();
string tempString2;
for (int i = 0; i < benchmarkIterations; i++)
{
tempString2 = sourceString;
tempString2 = tempString2.Replace(oldValue, newValue,
StringComparison.OrdinalIgnoreCase);
totalLenght = totalLenght + tempString2.Length;
}
currentSolutionStopwatch.Stop();
Original idea – #Darky711; thanks #MinerR for StringBuilder.
Lots of suggestions using Regex. How about this extension method without it:
public static string Replace(this string str, string old, string #new, StringComparison comparison)
{
#new = #new ?? "";
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(old) || old.Equals(#new, comparison))
return str;
int foundAt = 0;
while ((foundAt = str.IndexOf(old, foundAt, comparison)) != -1)
{
str = str.Remove(foundAt, old.Length).Insert(foundAt, #new);
foundAt += #new.Length;
}
return str;
}
Extensions make our lives easier:
static public class StringExtensions
{
static public string ReplaceInsensitive(this string str, string from, string to)
{
str = Regex.Replace(str, from, to, RegexOptions.IgnoreCase);
return str;
}
}
You can use the Microsoft.VisualBasic namespace to find this helper function:
Replace(sourceString, "replacethis", "withthis", , , CompareMethod.Text)
.Net Core has this method built-in:
Replace(String, String, StringComparison) Doc. Now we can simply write:
"...".Replace("oldValue", "newValue", StringComparison.OrdinalIgnoreCase)
Modified #Darky711's answer to use the passed in comparison type and match the framework replace naming and xml comments as closely as possible.
/// <summary>
/// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string.
/// </summary>
/// <param name="str">The string performing the replace method.</param>
/// <param name="oldValue">The string to be replaced.</param>
/// <param name="newValue">The string replace all occurrances of oldValue.</param>
/// <param name="comparisonType">Type of the comparison.</param>
/// <returns></returns>
public static string Replace(this string str, string oldValue, string #newValue, StringComparison comparisonType)
{
#newValue = #newValue ?? string.Empty;
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(oldValue) || oldValue.Equals(#newValue, comparisonType))
{
return str;
}
int foundAt;
while ((foundAt = str.IndexOf(oldValue, 0, comparisonType)) != -1)
{
str = str.Remove(foundAt, oldValue.Length).Insert(foundAt, #newValue);
}
return str;
}
(Edited: wasn't aware of the `naked link' problem, sorry about that)
Taken from here:
string myString = "find Me and replace ME";
string strReplace = "me";
myString = Regex.Replace(myString, "me", strReplace, RegexOptions.IgnoreCase);
Seems you are not the first to complain of the lack of case insensitive string.Replace.
I have wrote extension method:
public static string ReplaceIgnoreCase(this string source, string oldVale, string newVale)
{
if (source.IsNullOrEmpty() || oldVale.IsNullOrEmpty())
return source;
var stringBuilder = new StringBuilder();
string result = source;
int index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
while (index >= 0)
{
if (index > 0)
stringBuilder.Append(result.Substring(0, index));
if (newVale.IsNullOrEmpty().IsNot())
stringBuilder.Append(newVale);
stringBuilder.Append(result.Substring(index + oldVale.Length));
result = stringBuilder.ToString();
index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
}
return result;
}
I use two additional extension methods for previous extension method:
public static bool IsNullOrEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
public static bool IsNot(this bool val)
{
return val == false;
}
Doesn't this work: I cant imaging anything else being much quicker or easier.
public static class ExtensionMethodsString
{
public static string Replace(this String thisString, string oldValue, string newValue, StringComparison stringComparison)
{
string working = thisString;
int index = working.IndexOf(oldValue, stringComparison);
while (index != -1)
{
working = working.Remove(index, oldValue.Length);
working = working.Insert(index, newValue);
index = index + newValue.Length;
index = working.IndexOf(oldValue, index, stringComparison);
}
return working;
}
}
Extending Petrucio's answer with Regex.Escape on the search string, and escaping matched group as suggested in Steve B's answer (and some minor changes to my taste):
public static class StringExtensions
{
public static string ReplaceIgnoreCase(this string str, string from, string to)
{
return Regex.Replace(str, Regex.Escape(from), to.Replace("$", "$$"), RegexOptions.IgnoreCase);
}
}
Which will produce the following expected results:
Console.WriteLine("(heLLo) wOrld".ReplaceIgnoreCase("(hello) world", "Hi $1 Universe")); // Hi $1 Universe
Console.WriteLine("heLLo wOrld".ReplaceIgnoreCase("(hello) world", "Hi $1 Universe")); // heLLo wOrld
However without performing the escapes you would get the following, which is not an expected behaviour from a String.Replace that is just case-insensitive:
Console.WriteLine("(heLLo) wOrld".ReplaceIgnoreCase_NoEscaping("(hello) world", "Hi $1 Universe")); // (heLLo) wOrld
Console.WriteLine("heLLo wOrld".ReplaceIgnoreCase_NoEscaping("(hello) world", "Hi $1 Universe")); // Hi heLLo Universe
Using #Georgy Batalov solution I had a problem when using the following example
string original = "blah,DC=bleh,DC=blih,DC=bloh,DC=com";
string replaced = original.ReplaceIgnoreCase(",DC=", ".")
Below is how I rewrote his extension
public static string ReplaceIgnoreCase(this string source, string oldVale,
string newVale)
{
if (source.IsNullOrEmpty() || oldVale.IsNullOrEmpty())
return source;
var stringBuilder = new StringBuilder();
string result = source;
int index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
bool initialRun = true;
while (index >= 0)
{
string substr = result.Substring(0, index);
substr = substr + newVale;
result = result.Remove(0, index);
result = result.Remove(0, oldVale.Length);
stringBuilder.Append(substr);
index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
}
if (result.Length > 0)
{
stringBuilder.Append(result);
}
return stringBuilder.ToString();
}
My this Method could Ignore Case as well as Select Only Whole Word
public static string Replace(this string s, string word, string by, StringComparison stringComparison, bool WholeWord)
{
s = s + " ";
int wordSt;
StringBuilder sb = new StringBuilder();
while (s.IndexOf(word, stringComparison) > -1)
{
wordSt = s.IndexOf(word, stringComparison);
if (!WholeWord || ((wordSt == 0 || !Char.IsLetterOrDigit(char.Parse(s.Substring(wordSt - 1, 1)))) && !Char.IsLetterOrDigit(char.Parse(s.Substring(wordSt + word.Length, 1)))))
{
sb.Append(s.Substring(0, wordSt) + by);
}
else
{
sb.Append(s.Substring(0, wordSt + word.Length));
}
s = s.Substring(wordSt + word.Length);
}
sb.Append(s);
return sb.ToString().Substring(0, sb.Length - 1);
}
Another way is to ignore the case sensitivity in String.Replace() using the option StringComparison.CurrentCultureIgnoreCase
string.Replace("World", "csharp", StringComparison.CurrentCultureIgnoreCase)
I recommend the StringComparison.CurrentCultureIgnoreCase method as proposed by ZZY / Gama Sharma. This is just another technique that could be used with LINQ:
List<string> ItemsToRedact = new List<string> { "star", "citizen", "test", "universe"};
string Message = "Just like each sTaR is unique yet mAkes the uniVERSE what it is, the light in you makes you who you are";
List<string> ReplacementList = Message.Split(' ').Where(x => ItemsToRedact.Contains(x.ToLower())).ToList();
foreach (var word in ReplacementList)
{
Message = Message.Replace(word, "[Redacted] ");
}
Console.WriteLine(Message);
returns: Just like each [Redacted] is unique yet mAkes the [Redacted] what it is, the light in you makes you who you are
This code could be further distilled but I broke it up for readability
Below function is to remove all match word like (this) from the string set. By Ravikant Sonare.
private static void myfun()
{
string mystring = "thiTHISThiss This THIS THis tThishiThiss. Box";
var regex = new Regex("this", RegexOptions.IgnoreCase);
mystring = regex.Replace(mystring, "");
string[] str = mystring.Split(' ');
for (int i = 0; i < str.Length; i++)
{
if (regex.IsMatch(str[i].ToString()))
{
mystring = mystring.Replace(str[i].ToString(), string.Empty);
}
}
Console.WriteLine(mystring);
}
You can also try the Regex class.
var regex = new Regex( "camel", RegexOptions.IgnoreCase );
var newSentence = regex.Replace( sentence, "horse" );
Use this, Tested and 100% Worked!
For VB.NET
Dim myString As String
Dim oldValue As String
Dim newValue As String
myString = Form1.TextBox1.Text
oldValue = TextBox1.Text
newValue = TextBox2.Text
Dim working As String = myString
Dim index As Integer = working.IndexOf(oldValue, StringComparison.CurrentCultureIgnoreCase)
While index <> -1
working = working.Remove(index, oldValue.Length)
working = working.Insert(index, newValue)
index = index + newValue.Length
index = working.IndexOf(oldValue, index, StringComparison.CurrentCultureIgnoreCase)
Form1.TextBox1.Text = working
End While
For C#
private void Button2_Click(System.Object sender, System.EventArgs e)
{
string myString;
string oldValue;
string newValue;
myString = Form1.TextBox1.Text;
oldValue = TextBox1.Text;
newValue = TextBox2.Text;
string working = myString;
int index = working.IndexOf(oldValue, StringComparison.CurrentCultureIgnoreCase);
while (index != -1)
{
working = working.Remove(index, oldValue.Length);
working = working.Insert(index, newValue);
index = index + newValue.Length;
index = working.IndexOf(oldValue, index, StringComparison.CurrentCultureIgnoreCase);
Form1.TextBox1.Text = working;
}
}
I prefer this - "Hello World".ToLower().Replace( "world", "csharp" );

Categories

Resources