[英]Split a string with delimiters but keep the delimiters in the result in C#
我想用分隔符分割一个字符串,但在结果中保留分隔符。
我将如何在 C# 中做到这一点?
如果拆分字符是,
, .
, 和;
,我会尝试:
using System.Text.RegularExpressions;
...
string[] parts = Regex.Split(originalString, @"(?<=[.,;])")
(?<=PATTERN)
是正的向后看对于PATTERN
。 它应该在前面的文本适合PATTERN
任何地方匹配,因此在每次出现任何字符后都应该有一个匹配(和一个拆分)。
如果您希望分隔符成为其“自己的拆分”,则可以使用Regex.Split例如:
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'
因此,如果您正在寻找拆分数学公式,则可以使用以下正则表达式
@"([*()\^\/]|(?<!E)[\+\-])"
这将确保您还可以使用 1E-02 之类的常量,并避免将它们拆分为 1E、- 和 02
所以:
Regex.Split("10E-02*x+sin(x)^2", @"([*()\^\/]|(?<!E)[\+\-])")
产量:
10E-02
*
x
+
sin
(
x
)
^
2
根据 BFree 的回答,我有相同的目标,但我想拆分类似于原始 Split 方法的字符数组,并且每个字符串也有多个拆分:
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);
}
}
以防万一有人也想要这个答案......
而不是string[] parts = Regex.Split(originalString, @"(?<=[.,;])")
你可以使用string[] parts = Regex.Split(originalString, @"(?=yourmatch)")
你的yourmatch
是什么你的分隔符。
假设原始字符串是
777-猫
777 - 狗
777 - 鼠标
777 - 老鼠
777 - 狼
Regex.Split(originalString, @"(?=777)")
将返回
777 - 猫
777 - 狗
等等
此版本不使用 LINQ 或 Regex,因此它可能相对高效。 我认为它可能比 Regex 更容易使用,因为您不必担心转义特殊分隔符。 它返回一个IList<string>
,这比总是转换为数组更有效。 这是一种扩展方法,很方便。 您可以将分隔符作为数组或多个参数传入。
/// <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;
}
一些单元测试:
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], "]");
这个问题有很多答案! 我敲了一个被各种字符串拆分的字符串(原始答案仅适用于字符,即长度为 1)。 这还没有经过全面测试。
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;
}
这似乎有效,但它没有经过太多测试。
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();
}
为避免向新行添加字符,请尝试以下操作:
string[] substrings = Regex.Split(input,@"(?<=[-])");
我想说实现这一点的最简单方法(除了 Hans Kesting 提出的参数)是以常规方式拆分字符串,然后遍历数组并将分隔符添加到除最后一个元素之外的每个元素。
result = originalString.Split(separator);
for(int i = 0; i < result.Length - 1; i++)
result[i] += separator;
(编辑- 这是一个糟糕的答案 - 我误读了他的问题,并没有看到他被多个字符分割。)
(编辑 - 正确的 LINQ 版本很尴尬,因为分隔符不应连接到拆分数组中的最后一个字符串。)
最近我写了一个扩展方法来做这个:
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;
}
}
}
逐个字符地遍历字符串(无论如何,这就是正则表达式所做的。当您找到拆分器时,然后分离出一个子字符串。
伪代码
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;
}
}
这有点像 C#,但不是真的。 显然,选择适当的函数名称。 另外,我认为那里可能存在一个 1 的错误。
但这将满足您的要求。
veggerby 的答案修改为
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"
Regex.Escape()
只是为了防止您的分隔符包含正则表达式解释为特殊模式命令(如*
、 (
) 并且因此必须转义的字符)。
我想做一个像这样的多行字符串但需要保留换行符所以我这样做了
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);
}
产量
line 1 one
line 2 two
我遇到了同样的问题,但有多个分隔符。 这是我的解决方案:
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();
}
带有分隔 CamelCase 变量名称的示例:
var variableSplit = variableName.SplitLeft(
Enumerable.Range('A', 26).Select(i => (char)i).ToArray());
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());
}
}
}
}
我写了这段代码来拆分和保留分隔符:
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();
}
用法示例:
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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.