I would like to split a string with delimiters but keep the delimiters in the result.
How would I do this in C#?
If the split chars were ,, ., and ;, I'd try:
using System.Text.RegularExpressions;
...
string[] parts = Regex.Split(originalString, #"(?<=[.,;])")
(?<=PATTERN) is positive look-behind for PATTERN. It should match at any place where the preceding text fits PATTERN so there should be a match (and a split) after each occurrence of any of the characters.
If you want the delimiter to be its "own split", you can use Regex.Split e.g.:
string input = "plum-pear";
string pattern = "(-)";
string[] substrings = Regex.Split(input, pattern); // Split on hyphens
foreach (string match in substrings)
{
Console.WriteLine("'{0}'", match);
}
// The method writes the following to the console:
// 'plum'
// '-'
// 'pear'
So if you are looking for splitting a mathematical formula, you can use the following Regex
#"([*()\^\/]|(?<!E)[\+\-])"
This will ensure you can also use constants like 1E-02 and avoid having them split into 1E, - and 02
So:
Regex.Split("10E-02*x+sin(x)^2", #"([*()\^\/]|(?<!E)[\+\-])")
Yields:
10E-02
*
x
+
sin
(
x
)
^
2
Building off from BFree's answer, I had the same goal, but I wanted to split on an array of characters similar to the original Split method, and I also have multiple splits per string:
public static IEnumerable<string> SplitAndKeep(this string s, char[] delims)
{
int start = 0, index;
while ((index = s.IndexOfAny(delims, start)) != -1)
{
if(index-start > 0)
yield return s.Substring(start, index - start);
yield return s.Substring(index, 1);
start = index + 1;
}
if (start < s.Length)
{
yield return s.Substring(start);
}
}
Just in case anyone wants this answer aswell...
Instead of string[] parts = Regex.Split(originalString, #"(?<=[.,;])") you could use string[] parts = Regex.Split(originalString, #"(?=yourmatch)") where yourmatch is whatever your separator is.
Supposing the original string was
777- cat
777 - dog
777 - mouse
777 - rat
777 - wolf
Regex.Split(originalString, #"(?=777)") would return
777 - cat
777 - dog
and so on
This version does not use LINQ or Regex and so it's probably relatively efficient. I think it might be easier to use than the Regex because you don't have to worry about escaping special delimiters. It returns an IList<string> which is more efficient than always converting to an array. It's an extension method, which is convenient. You can pass in the delimiters as either an array or as multiple parameters.
/// <summary>
/// Splits the given string into a list of substrings, while outputting the splitting
/// delimiters (each in its own string) as well. It's just like String.Split() except
/// the delimiters are preserved. No empty strings are output.</summary>
/// <param name="s">String to parse. Can be null or empty.</param>
/// <param name="delimiters">The delimiting characters. Can be an empty array.</param>
/// <returns></returns>
public static IList<string> SplitAndKeepDelimiters(this string s, params char[] delimiters)
{
var parts = new List<string>();
if (!string.IsNullOrEmpty(s))
{
int iFirst = 0;
do
{
int iLast = s.IndexOfAny(delimiters, iFirst);
if (iLast >= 0)
{
if (iLast > iFirst)
parts.Add(s.Substring(iFirst, iLast - iFirst)); //part before the delimiter
parts.Add(new string(s[iLast], 1));//the delimiter
iFirst = iLast + 1;
continue;
}
//No delimiters were found, but at least one character remains. Add the rest and stop.
parts.Add(s.Substring(iFirst, s.Length - iFirst));
break;
} while (iFirst < s.Length);
}
return parts;
}
Some unit tests:
text = "[a link|http://www.google.com]";
result = text.SplitAndKeepDelimiters('[', '|', ']');
Assert.IsTrue(result.Count == 5);
Assert.AreEqual(result[0], "[");
Assert.AreEqual(result[1], "a link");
Assert.AreEqual(result[2], "|");
Assert.AreEqual(result[3], "http://www.google.com");
Assert.AreEqual(result[4], "]");
A lot of answers to this! One I knocked up to split by various strings (the original answer caters for just characters i.e. length of 1). This hasn't been fully tested.
public static IEnumerable<string> SplitAndKeep(string s, params string[] delims)
{
var rows = new List<string>() { s };
foreach (string delim in delims)//delimiter counter
{
for (int i = 0; i < rows.Count; i++)//row counter
{
int index = rows[i].IndexOf(delim);
if (index > -1
&& rows[i].Length > index + 1)
{
string leftPart = rows[i].Substring(0, index + delim.Length);
string rightPart = rows[i].Substring(index + delim.Length);
rows[i] = leftPart;
rows.Insert(i + 1, rightPart);
}
}
}
return rows;
}
This seems to work, but its not been tested much.
public static string[] SplitAndKeepSeparators(string value, char[] separators, StringSplitOptions splitOptions)
{
List<string> splitValues = new List<string>();
int itemStart = 0;
for (int pos = 0; pos < value.Length; pos++)
{
for (int sepIndex = 0; sepIndex < separators.Length; sepIndex++)
{
if (separators[sepIndex] == value[pos])
{
// add the section of string before the separator
// (unless its empty and we are discarding empty sections)
if (itemStart != pos || splitOptions == StringSplitOptions.None)
{
splitValues.Add(value.Substring(itemStart, pos - itemStart));
}
itemStart = pos + 1;
// add the separator
splitValues.Add(separators[sepIndex].ToString());
break;
}
}
}
// add anything after the final separator
// (unless its empty and we are discarding empty sections)
if (itemStart != value.Length || splitOptions == StringSplitOptions.None)
{
splitValues.Add(value.Substring(itemStart, value.Length - itemStart));
}
return splitValues.ToArray();
}
Recently I wrote an extension method do to this:
public static class StringExtensions
{
public static IEnumerable<string> SplitAndKeep(this string s, string seperator)
{
string[] obj = s.Split(new string[] { seperator }, StringSplitOptions.None);
for (int i = 0; i < obj.Length; i++)
{
string result = i == obj.Length - 1 ? obj[i] : obj[i] + seperator;
yield return result;
}
}
}
I'd say the easiest way to accomplish this (except for the argument Hans Kesting brought up) is to split the string the regular way, then iterate over the array and add the delimiter to every element but the last.
To avoid adding character to new line try this :
string[] substrings = Regex.Split(input,#"(?<=[-])");
result = originalString.Split(separator);
for(int i = 0; i < result.Length - 1; i++)
result[i] += separator;
(EDIT - this is a bad answer - I misread his question and didn't see that he was splitting by multiple characters.)
(EDIT - a correct LINQ version is awkward, since the separator shouldn't get concatenated onto the final string in the split array.)
Iterate through the string character by character (which is what regex does anyway.
When you find a splitter, then spin off a substring.
pseudo code
int hold, counter;
List<String> afterSplit;
string toSplit
for(hold = 0, counter = 0; counter < toSplit.Length; counter++)
{
if(toSplit[counter] = /*split charaters*/)
{
afterSplit.Add(toSplit.Substring(hold, counter));
hold = counter;
}
}
That's sort of C# but not really. Obviously, choose the appropriate function names.
Also, I think there might be an off-by-1 error in there.
But that will do what you're asking.
veggerby's answer modified to
have no string items in the list
have fixed string as delimiter like "ab" instead of single character
var delimiter = "ab";
var text = "ab33ab9ab"
var parts = Regex.Split(text, $#"({Regex.Escape(delimiter)})")
.Where(p => p != string.Empty)
.ToList();
// parts = "ab", "33", "ab", "9", "ab"
The Regex.Escape() is there just in case your delimiter contains characters which regex interprets as special pattern commands (like *, () and thus have to be escaped.
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
string input = #"This;is:a.test";
char sep0 = ';', sep1 = ':', sep2 = '.';
string pattern = string.Format("[{0}{1}{2}]|[^{0}{1}{2}]+", sep0, sep1, sep2);
Regex regex = new Regex(pattern);
MatchCollection matches = regex.Matches(input);
List<string> parts=new List<string>();
foreach (Match match in matches)
{
parts.Add(match.ToString());
}
}
}
}
I wanted to do a multiline string like this but needed to keep the line breaks so I did this
string x =
#"line 1 {0}
line 2 {1}
";
foreach(var line in string.Format(x, "one", "two")
.Split("\n")
.Select(x => x.Contains('\r') ? x + '\n' : x)
.AsEnumerable()
) {
Console.Write(line);
}
yields
line 1 one
line 2 two
I came across same problem but with multiple delimiters. Here's my solution:
public static string[] SplitLeft(this string #this, char[] delimiters, int count)
{
var splits = new List<string>();
int next = -1;
while (splits.Count + 1 < count && (next = #this.IndexOfAny(delimiters, next + 1)) >= 0)
{
splits.Add(#this.Substring(0, next));
#this = new string(#this.Skip(next).ToArray());
}
splits.Add(#this);
return splits.ToArray();
}
Sample with separating CamelCase variable names:
var variableSplit = variableName.SplitLeft(
Enumerable.Range('A', 26).Select(i => (char)i).ToArray());
I wrote this code to split and keep delimiters:
private static string[] SplitKeepDelimiters(string toSplit, char[] delimiters, StringSplitOptions splitOptions = StringSplitOptions.None)
{
var tokens = new List<string>();
int idx = 0;
for (int i = 0; i < toSplit.Length; ++i)
{
if (delimiters.Contains(toSplit[i]))
{
tokens.Add(toSplit.Substring(idx, i - idx)); // token found
tokens.Add(toSplit[i].ToString()); // delimiter
idx = i + 1; // start idx for the next token
}
}
// last token
tokens.Add(toSplit.Substring(idx));
if (splitOptions == StringSplitOptions.RemoveEmptyEntries)
{
tokens = tokens.Where(token => token.Length > 0).ToList();
}
return tokens.ToArray();
}
Usage example:
string toSplit = "AAA,BBB,CCC;DD;,EE,";
char[] delimiters = new char[] {',', ';'};
string[] tokens = SplitKeepDelimiters(toSplit, delimiters, StringSplitOptions.RemoveEmptyEntries);
foreach (var token in tokens)
{
Console.WriteLine(token);
}
This question already has answers here:
A faster way of doing multiple string replacements
(8 answers)
Closed 9 years ago.
I want to replace some invalid characters in the name of a file uploaded to my application.
I've searched up to something on the internet and found some complex algorithms to do it, here's one:
public static string RemoverAcentuacao(string palavra)
{
string palavraSemAcento = null;
string caracterComAcento = "áàãâäéèêëíìîïóòõôöúùûüçáàãâÄéèêëíìîïóòõÖôúùûÜç, ?&:/!;ºª%‘’()\"”“";
string caracterSemAcento = "aaaaaeeeeiiiiooooouuuucAAAAAEEEEIIIIOOOOOUUUUC___________________";
if (!String.IsNullOrEmpty(palavra))
{
for (int i = 0; i < palavra.Length; i++)
{
if (caracterComAcento.IndexOf(Convert.ToChar(palavra.Substring(i, 1))) >= 0)
{
int car = caracterComAcento.IndexOf(Convert.ToChar(palavra.Substring(i, 1)));
palavraSemAcento += caracterSemAcento.Substring(car, 1);
}
else
{
palavraSemAcento += palavra.Substring(i, 1);
}
}
string[] cEspeciais = { "#39", "---", "--", "'", "#", "\r\n", "\n", "\r" };
for (int q = 0; q < cEspeciais.Length; q++)
{
palavraSemAcento = palavraSemAcento.Replace(cEspeciais[q], "-");
}
for (int x = (cEspeciais.Length - 1); x > -1; x--)
{
palavraSemAcento = palavraSemAcento.Replace(cEspeciais[x], "-");
}
palavraSemAcento = palavraSemAcento.Replace("+", "-").Replace(Environment.NewLine, "").TrimStart('-').TrimEnd('-').Replace("<i>", "-").Replace("<-i>", "-").Replace("<br>", "").Replace("--", "-");
}
else
{
palavraSemAcento = "indefinido";
}
return palavraSemAcento.ToLower();
}
There's a way to do it with a less complex algorithm?
I think this algorithm is very complex to something not too complex, but I can't think in something diferent of this.
I want to replace some invalid characters in the name of a file
if this is really what you want then it is easy
string ToLegalFileName(string s)
{
var invalidChars = new HashSet<char>(Path.GetInvalidFileNameChars());
return String.Join("", s.Select(c => invalidChars.Contains(c) ? '_' : c));
}
if your intent is to replace accented chars with their ascii counterparts then
string RemoverAcentuacao(string s)
{
return String.Join("",
s.Normalize(NormalizationForm.FormD)
.Where(c => char.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark));
}
and this is the 3rd version which replaces accented chars + other chars with '_'
string RemoverAcentuacao2(string s)
{
return String.Join("",
s.Normalize(NormalizationForm.FormD)
.Where(c => char.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
.Select(c => char.IsLetterOrDigit(c) ? c : '_')
.Select(c => (int)c < 128 ? c : '_'));
}
A solution using regular expressions:
string ReplaceSpecial(string input, string replace, char replacewith)
{
char[] back = input.ToCharArray();
var matches = Regex.Matches(String.Format("[{0}]", replace), input);
foreach (var i in matches)
back[i.Index] = replacewith;
return new string(back);
}
A somewhat simpler solution using String.Replace:
string ReplaceSpecial(string input, char[] replace, char replacewith)
{
string back = input;
foreach (char i in replace)
back.Replace(i, replacewith);
return back;
}
static string RemoverAcentuacao(string s)
{
string caracterComAcento = "áàãâäéèêëíìîïóòõôöúùûüçáàãâÄéèêëíìîïóòõÖôúùûÜç, ?&:/!;ºª%‘’()\"”“";
string caracterSemAcento = "aaaaaeeeeiiiiooooouuuucAAAAAEEEEIIIIOOOOOUUUUC___________________";
return new String(s.Select(c =>
{
int i = caracterComAcento.IndexOf(c);
return (i == -1) ? c : caracterSemAcento[i];
}).ToArray());
}
Here is a really simple method that I've used recently.
I hope it meets your requirements. To be honest, the code is a bit difficult to read due to the language of the variable declarations.
List<char> InvalidCharacters = new List<char>() { 'a','b','c' };
static string StripInvalidCharactersFromField(string field)
{
for (int i = 0; i < field.Length; i++)
{
string s = new string(new char[] { field[i] });
if (InvalidCharacters.Contains(s))
{
field = field.Remove(i, 1);
i--;
}
}
return field;
}
Hello I have a long string. I want to split it as some kind of format that has many return carrages.
Each line has 5 short words.
Ex.
string input="'250.0','250.00','250.01','250.02','250.03','250.1','250.10','250.11','250.12','250.13','250.2','250.20','250.21','250.22','250.23','250.3','250.30','250.31','250.32','250.33','250.4','250.40','250.41','250.42','250.43','250.5','250.50','250.51','250.52','250.53','250.6','250.60','250.61','250.62','250.63','250.7','250.70','250.71','250.72','250.73','250.8','250.80','250.81','250.82','250.83','250.9','250.90','250.91','250.92','250.93','357.2','357.20','362.01','362.02','362.03','362.04','362.05','362.06','362.07','366.41','648.0','648.00','648.01','648.02','648.03','648.04'";
It has 66 short words.
string output = "'250.0','250.00','250.01','250.02','250.03',
'250.1','250.10','250.11','250.12','250.13',
'250.2','250.20','250.21','250.22','250.23',
'250.3','250.30','250.31','250.32','250.33',
'250.4','250.40','250.41','250.42','250.43',
'250.5','250.50','250.51','250.52','250.53',
'250.6','250.60','250.61','250.62','250.63',
'250.7','250.70','250.71','250.72','250.73',
'250.8','250.80','250.81','250.82','250.83',
'250.9','250.90','250.91','250.92','250.93',
'357.2','357.20','362.01','362.02','362.03',
'362.04','362.05','362.06','362.07','366.41',
'648.0','648.00','648.01','648.02','648.03',
'648.04'";
I thought that I have to count char ',' in the string first such as in the example. But it could be kind of clumsy.
Thanks for advice.
If i've understood you correctly you want to
split those words by comma
group the result into lines where each line contains 5 words
build a string with Environment.NewLine as separator
string input = "'250.0','250.00','250.01','250.02','250.03','250.1','250.10','250.11','250.12','250.13','250.2','250.20','250.21','250.22','250.23','250.3','250.30','250.31','250.32','250.33','250.4','250.40','250.41','250.42','250.43','250.5','250.50','250.51','250.52','250.53','250.6','250.60','250.61','250.62','250.63','250.7','250.70','250.71','250.72','250.73','250.8','250.80','250.81','250.82','250.83','250.9','250.90','250.91','250.92','250.93','357.2','357.20','362.01','362.02','362.03','362.04','362.05','362.06','362.07','366.41','648.0','648.00','648.01','648.02','648.03','648.04'";
int groupCount = 5;
var linesGroups = input.Split(',')
.Select((s, index) => new { str = s, Position = index / groupCount, Index = index })
.GroupBy(x => x.Position);
StringBuilder outputBuilder = new StringBuilder();
foreach (var grp in linesGroups)
{
outputBuilder.AppendLine(String.Join(",", grp.Select(x => x.str)));
}
String output = outputBuilder.ToString();
Edit: The result is:
'250.0','250.00','250.01','250.02','250.03'
'250.1','250.10','250.11','250.12','250.13'
'250.2','250.20','250.21','250.22','250.23'
'250.3','250.30','250.31','250.32','250.33'
'250.4','250.40','250.41','250.42','250.43'
'250.5','250.50','250.51','250.52','250.53'
'250.6','250.60','250.61','250.62','250.63'
'250.7','250.70','250.71','250.72','250.73'
'250.8','250.80','250.81','250.82','250.83'
'250.9','250.90','250.91','250.92','250.93'
'357.2','357.20','362.01','362.02','362.03'
'362.04','362.05','362.06','362.07','366.41'
'648.0','648.00','648.01','648.02','648.03'
'648.04'
If you want to append every line with a comma(like in your example):
foreach (var grp in linesGroups)
{
outputBuilder.AppendLine(String.Join(",", grp.Select(x => x.str)) + ",");
}
// remove last comma + Environment.NewLine
outputBuilder.Length -= ( 1 + Environment.NewLine.Length );
How about this solution:
private static IEnumerable<string> SplitLongString(string input, char separator, int groupSize)
{
int indexCurrent = 0;
int indexLastOccurence = 0;
int separatorCounter = 0;
foreach (var character in input)
{
indexCurrent++;
if (character == separator)
{
separatorCounter++;
if (separatorCounter % groupSize == 0)
{
yield return input.Substring(indexLastOccurence, indexCurrent - indexLastOccurence);
indexLastOccurence = indexCurrent;
}
}
}
if (indexCurrent != indexLastOccurence)
{
yield return input.Substring(indexLastOccurence, indexCurrent - indexLastOccurence);
}
}
And you would call it:
var result = SplitLongString(input, ',', 5);
foreach (var row in result)
{
Console.WriteLine(row);
}
The simplest way would be to follow the approach from #Tim's deleted (edit: not deleted any more) answer:
Split the string into parts by comma (using string.Split)
Rearrange the obtained parts in any way you need: for example, packing by 5 in a line.
Something like that (not tested):
Console.WriteLine("string output =");
var parts = sourceString.Split(',');
int i = 0;
for (; i < parts.Length; i++)
{
if (i % 5 == 0)
Console.Write(' "');
Console.Write(parts[i]);
Console.Write(',');
if (i % 5 == 4 && i != parts.Length - 1)
Console.WriteLine('" +');
}
Console.WriteLine('";');
var input="'250.0','250.00','250.01','250.02','250.03','250.1','250.10','250.11','250.12','250.13','250.2','250.20','250.21','250.22','250.23','250.3','250.30','250.31','250.32','250.33','250.4','250.40','250.41','250.42','250.43','250.5','250.50','250.51','250.52','250.53','250.6','250.60','250.61','250.62','250.63','250.7','250.70','250.71','250.72','250.73','250.8','250.80','250.81','250.82','250.83','250.9','250.90','250.91','250.92','250.93','357.2','357.20','362.01','362.02','362.03','362.04','362.05','362.06','362.07','366.41','648.0','648.00','648.01','648.02','648.03','648.04'";
var wordsArray = input.Split(',');
var sbOutput = new StringBuilder();
for (int i = 1; i < wordsArray.Length +1; i++)
{
sbOutput.AppendFormat("{0},", wordsArray[i-1]);
if(i % 5 == 0)
sbOutput.AppendLine();
}
var output = sbOutput.ToString();
Do something like this:
string[] words = input.Split(',');
int wordsInString = words.Length;