【问题标题】:Split a string with delimiters but keep the delimiters in the result in C#使用分隔符拆分字符串,但将分隔符保留在 C# 中的结果中
【发布时间】:2011-01-13 12:41:08
【问题描述】:

我想用分隔符分割字符串,但将分隔符保留在结果中。

我将如何在 C# 中执行此操作?

【问题讨论】:

  • 对于“a|b”,你想要“a|”+“b”还是“a”+“|b”还是“a|”+“|b”还是别的?简而言之:分隔符属于哪个段?
  • 将分隔符保留在什么结果中?您希望分隔符作为每个拆分字符串的一部分吗?你的问题很模糊。
  • 嘿,我想从字符列表中删除一个字符串,字符串的结果也将包含分隔符。作为 veggerby 下面的建议是我想要实现的目标。我先测试一下\

标签: c# string split


【解决方案1】:

如果拆分字符是 ,.;,我会尝试:

using System.Text.RegularExpressions;
...    
string[] parts = Regex.Split(originalString, @"(?<=[.,;])")

(?&lt;=PATTERN)PATTERN 的正向回溯。它应该在前面文本适合PATTERN 的任何地方匹配,因此在每次出现任何字符之后都应该有一个匹配(和一个拆分)。

【讨论】:

  • 这对我很有用 - 谢谢!我只需要为我的目的进行一点调整,因为我想在每行的开头(而不是结尾)包含分隔符。请改用@"(?=[.,;])"。
  • 这个答案需要被接受,这样更容易访问。我是一位经验丰富的 SO 用户,我花了一段时间才找到。
  • 嗨@it-depends 我喜欢你的回答,但是如果我想用字符串而不是字符来分割它怎么办。例如,您使用的所有分隔符后跟一个空格。我已经尝试过了,但没有用。 @"(?
  • @roberto-zamora 当我有时间让它更通用时,我会正确更新答案。但你可能想尝试 (?
  • 如果您想将分隔符保留在它们自己的部分(而不是分隔部分的开头或结尾),您也可以使用@"([.,;])"。根据msdn.microsoft.com/en-us/library/…,“如果在 Regex.Split 表达式中使用捕获括号,则任何捕获的文本都将包含在结果字符串数组中。”
【解决方案2】:

如果您希望分隔符成为其“自己的拆分”,您可以使用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

【讨论】:

  • 嗨。谢谢。这就是我想要的。我会先测试一下。谢谢。但是有没有比使用 Regex 更好的方法?
  • 嗨,因为我的模式是例如:char[] chars = new char[]{'A','B','C'}。是否可以将 Regex 中的 Split 用于我的 char 数组而不是字符串模式?提前致谢
  • 嗨,如果我的模式包含 4 个运算符怎么办:+、-、* 和 / 它看起来如何?谢谢
  • 实际上,如果您正在解析/拆分数学表达式,您可以执行类似 @"([*()\^\/]|(?
  • 潜在问题:他在示例中使用的括号是必需的。 Regex.Split("plum-pear", "-") 只产生 'plum' 和 'pear'。编程很有趣。
【解决方案3】:

根据 BFree 的回答,我有相同的目标,但我想拆分一个类似于原始拆分方法的字符数组,并且每个字符串也有多个拆分:

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);
    }
}

【讨论】:

  • +1 它在数组索引中包含分隔符,因为它听起来像 OP 想要的。
  • 没有什么特别的原因,我只是没有看到一个简单的“交换”操作可用。它可以被许多替代交换方法所取代。
  • if (start
  • @Marko:谢谢,我已经根据你的建议更新了代码。
  • 您也可以将++ 内联为+ 1,因为无论如何您都在覆盖。所以Interlocked.Exchange(ref start, index + 1);.
【解决方案4】:

以防万一有人也想要这个答案......

您可以使用string[] parts = Regex.Split(originalString, @"(?=yourmatch)") 而不是string[] parts = Regex.Split(originalString, @"(?&lt;=[.,;])"),其中yourmatch 是您的分隔符。

假设原来的字符串是

777-猫

777 - 狗

777 - 鼠标

777 - 老鼠

777 - 狼

Regex.Split(originalString, @"(?=777)") 会返回

777 - 猫

777 - 狗

等等

【讨论】:

  • 如何指定分隔符列表?即“777、666等”
  • @Thomas 如果我没记错的话,您可以使用| 标记来指定替代方案。所以它会像:(?=777|666)
  • 我想用\r\n分隔,所以字符串first line\r\nsecond line会变成[0] - first line\r\n [1] - second line。使用您的解决方案后,我得到[0] - first line [1] - \r\nsecond line。将您的解决方案 @"(?=\r\n)" 更改为 @"(?&lt;=\r\n)" 后解决了它。非常感谢:) +1
  • 感谢您的建议。我想用'['分隔符分割字符串。Regex.Split(originalString, @"(?=\[)")为我工作。
【解决方案5】:

此版本不使用 LINQ 或 Regex,因此它可能相对高效。我认为它可能比 Regex 更容易使用,因为您不必担心转义特殊分隔符。它返回一个IList&lt;string&gt;,这比总是转换为数组更有效。这是一种扩展方法,很方便。您可以将分隔符作为数组或多个参数传入。

/// <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], "]");

【讨论】:

  • 这是一个很好的解决方案。如果我的分隔符是一个字符串呢?您能否也提供一个实现。
【解决方案6】:

很多答案!我敲了一个按各种字符串拆分(原始答案仅适用于字符,即长度为 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;
}

【讨论】:

    【解决方案7】:

    这似乎有效,但它没有经过太多测试。

    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();
    }
    

    【讨论】:

      【解决方案8】:

      为了避免在新行中添加字符,试试这个:

       string[] substrings = Regex.Split(input,@"(?<=[-])");
      

      【讨论】:

      • 并将分隔符添加到下一行的开头使用:(?=[-])
      【解决方案9】:
      result = originalString.Split(separator);
      for(int i = 0; i < result.Length - 1; i++)
          result[i] += separator;
      

      编辑 - 这是一个糟糕的答案 - 我误读了他的问题,没有看到他被多个字符分开。)

      (编辑 - 正确的 LINQ 版本很尴尬,因为分隔符不应连接到拆分数组中的最终字符串。)

      【讨论】:

      • 这仅适用于只有一个分隔符的情况。您可能需要使用一些正则表达式魔法。
      【解决方案10】:

      最近我为此写了一个扩展方法:

      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;
                  }
              }
          }
      

      【讨论】:

        【解决方案11】:

        逐个字符地遍历字符串(这就是正则表达式的作用。 当你找到一个拆分器时,然后拆分一个子字符串。

        伪代码

        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#,但不是真的。显然,选择适当的函数名称。 另外,我认为那里可能有一个 off-by-1 错误。

        但这会满足你的要求。

        【讨论】:

          【解决方案12】:

          我想说实现这一点的最简单方法(除了 Hans Kesting 提出的参数)是以常规方式拆分字符串,然后遍历数组并将分隔符添加到除最后一个元素之外的每个元素。

          【讨论】:

          • 这仅适用于您有 1 个分隔符的情况。如果我想在空格 换行符上拆分,我不知道要添加哪个。
          【解决方案13】:

          veggerby 的回答修改为

          • 列表中没有字符串项
          • 固定字符串作为分隔符,如“ab”而不是单个字符
          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() 是为了防止您的分隔符包含正则表达式解释为特殊模式命令的字符(如 *(),因此必须转义。

          【讨论】:

            【解决方案14】:
            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());
                        }
                    }
                }
            }
            

            【讨论】:

              【解决方案15】:

              我想做一个这样的多行字符串,但需要保留换行符,所以我这样做了

              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
              

              【讨论】:

                【解决方案16】:

                我遇到了同样的问题,但有多个分隔符。这是我的解决方案:

                    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());
                

                【讨论】:

                  【解决方案17】:

                  我写了这段代码来分割并保留分隔符

                  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);
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2011-06-08
                    • 2015-02-26
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-01-16
                    • 2018-12-23
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多