C#扩展方法 – 也接受转义字符的字符串拆分

我想为.NET String类编写扩展方法。 我希望它是Split方法的一个特殊的方法 – 一个采用转义字符来防止在分隔符之前使用转义字符时拆分字符串的方法。

写这个的最好方法是什么? 我很好奇最好的非正则表达式来接近它。
像签名一样的东西……

public static string[] Split(this string input, string separator, char escapeCharacter) { // ... } 

更新:因为它出现了一个评论,逃避……

在C#中转义非特殊字符时会出现错误 – CS1009:无法识别的转义序列。

在IE JScript中,转义转义字符。 除非你尝试\ u然后你得到一个“预期的hex数字”错误。 我测试了Firefox,它有相同的行为。

我希望这种方法非常宽容并遵循JavaScript模型。 如果你在非分隔符上转义,它应该“善意”删除转义字符。

怎么样:

 public static IEnumerable Split(this string input, string separator, char escapeCharacter) { int startOfSegment = 0; int index = 0; while (index < input.Length) { index = input.IndexOf(separator, index); if (index > 0 && input[index-1] == escapeCharacter) { index += separator.Length; continue; } if (index == -1) { break; } yield return input.Substring(startOfSegment, index-startOfSegment); index += separator.Length; startOfSegment = index; } yield return input.Substring(startOfSegment); } 

这似乎有效(有一些快速测试字符串),但它不会删除转义字符 – 这取决于你的确切情况,我怀疑。

这将需要清理一下,但这基本上是….

 List output = new List(); for(int i=0; i 

我的第一个观察是分隔符应该是char而不是字符串,因为使用单个字符转义字符串可能很难 – 转义字符覆盖了以下字符串的多少? 除此之外,@ James Curran的回答几乎就是我如何处理它 – 不过,正如他所说,它需要一些清理。 例如,在循环初始值设定项中将j初始化为0。 弄清楚如何处理空输入等

您可能还希望支持StringSplitOptions并指定是否应在集合中返回空字符串。

如果要删除转义字符,这是解决方案。

 public static IEnumerable Split(this string input, string separator, char escapeCharacter) { string[] splitted = input.Split(new[] { separator }); StringBuilder sb = null; foreach (string subString in splitted) { if (subString.EndsWith(escapeCharacter.ToString())) { if (sb == null) sb = new StringBuilder(); sb.Append(subString, 0, subString.Length - 1); } else { if (sb == null) yield return subString; else { sb.Append(subString); yield return sb.ToString(); sb = null; } } } if (sb != null) yield return sb.ToString(); } 

你可以尝试这样的事情。 虽然,我建议使用不安全的代码来执行性能关键任务。

 public static class StringExtensions { public static string[] Split(this string text, char escapeChar, params char[] seperator) { return Split(text, escapeChar, seperator, int.MaxValue, StringSplitOptions.None); } public static string[] Split(this string text, char escapeChar, char[] seperator, int count) { return Split(text, escapeChar, seperator, count, StringSplitOptions.None); } public static string[] Split(this string text, char escapeChar, char[] seperator, StringSplitOptions options) { return Split(text, escapeChar, seperator, int.MaxValue, options); } public static string[] Split(this string text, char escapeChar, char[] seperator, int count, StringSplitOptions options) { if (text == null) { throw new ArgumentNullException("text"); } if (text.Length == 0) { return new string[0]; } var segments = new List(); bool previousCharIsEscape = false; var segment = new StringBuilder(); for (int i = 0; i < text.Length; i++) { if (previousCharIsEscape) { previousCharIsEscape = false; if (seperator.Contains(text[i])) { // Drop the escape character when it escapes a seperator character. segment.Append(text[i]); continue; } // Retain the escape character when it escapes any other character. segment.Append(escapeChar); segment.Append(text[i]); continue; } if (text[i] == escapeChar) { previousCharIsEscape = true; continue; } if (seperator.Contains(text[i])) { if (options != StringSplitOptions.RemoveEmptyEntries || segment.Length != 0) { // Only add empty segments when options allow. segments.Add(segment.ToString()); } segment = new StringBuilder(); continue; } segment.Append(text[i]); } if (options != StringSplitOptions.RemoveEmptyEntries || segment.Length != 0) { // Only add empty segments when options allow. segments.Add(segment.ToString()); } return segments.ToArray(); } } 
 public static string[] Split(this string input, string separator, char escapeCharacter) { Guid g = Guid.NewGuid(); input = input.Replace(escapeCharacter.ToString() + separator, g.ToString()); string[] result = input.Split(new string []{separator}, StringSplitOptions.None); for (int i = 0; i < result.Length; i++) { result[i] = result[i].Replace(g.ToString(), escapeCharacter.ToString() + separator); } return result; } 

可能不是最好的方式,但它是另一种选择。 基本上,无论在哪里找到escape + seperator的序列,都要用GUID替换它(你可以在这里使用任何其他随机垃圾,无关紧要)。 然后使用内置的拆分function。 然后使用escape + seperator替换数组的每个元素中的guid。

签名不正确,您需要返回一个字符串数组

WARNIG从不使用扩展,所以原谅我一些错误;)

 public static List Split(this string input, string separator, char escapeCharacter) { String word = ""; List result = new List(); for (int i = 0; i < input.Length; i++) { //can also use switch if (input[i] == escapeCharacter) { break; } else if (input[i] == separator) { result.Add(word); word = ""; } else { word += input[i]; } } return result; } 

我个人欺骗并偷看了string.Split使用reflection器…… InternalSplitOmitEmptyEntries看起来很有用;-)

我也有这个问题,但没有找到解决方案。 所以我自己写了这样一个方法:

  public static IEnumerable Split( this string text, char separator, char escapeCharacter) { var builder = new StringBuilder(text.Length); bool escaped = false; foreach (var ch in text) { if (separator == ch && !escaped) { yield return builder.ToString(); builder.Clear(); } else { // separator is removed, escape characters are kept builder.Append(ch); } // set escaped for next cycle, // or reset unless escape character is escaped. escaped = escapeCharacter == ch && !escaped; } yield return builder.ToString(); } 

它与Escape和Unescape结合使用,它会转义分隔符和转义字符,并再次删除转义字符:

  public static string Escape(this string text, string controlChars, char escapeCharacter) { var builder = new StringBuilder(text.Length + 3); foreach (var ch in text) { if (controlChars.Contains(ch)) { builder.Append(escapeCharacter); } builder.Append(ch); } return builder.ToString(); } public static string Unescape(string text, char escapeCharacter) { var builder = new StringBuilder(text.Length); bool escaped = false; foreach (var ch in text) { escaped = escapeCharacter == ch && !escaped; if (!escaped) { builder.Append(ch); } } return builder.ToString(); } 

escape / unescape的例子

 separator = ',' escapeCharacter = '\\' //controlCharacters is always separator + escapeCharacter @"AB,CD\EF\," <=> @"AB\,CD\\EF\\\," 

分裂:

 @"AB,CD\,EF\\,GH\\\,IJ" => [@"AB", @"CD\,EF\\", @"GH\\\,IJ"] 

所以要使用它,在加入之前逃脱,在拆分之后使用Unescape。

 public string RemoveMultipleDelimiters(string sSingleLine) { string sMultipleDelimitersLine = ""; string sMultipleDelimitersLine1 = ""; int iDelimeterPosition = -1; iDelimeterPosition = sSingleLine.IndexOf('>'); iDelimeterPosition = sSingleLine.IndexOf('>', iDelimeterPosition + 1); if (iDelimeterPosition > -1) { sMultipleDelimitersLine = sSingleLine.Substring(0, iDelimeterPosition - 1); sMultipleDelimitersLine1 = sSingleLine.Substring(sSingleLine.IndexOf('>', iDelimeterPosition) - 1); sMultipleDelimitersLine1 = sMultipleDelimitersLine1.Replace('>', '*'); sSingleLine = sMultipleDelimitersLine + sMultipleDelimitersLine1; } return sSingleLine; }