簡體   English   中英

用分隔符拆分字符串,但將分隔符保留在 C# 中的結果中

[英]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 的答案修改為

  • 列表中沒有字符串項
  • 有固定的字符串作為分隔符,如“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()只是為了防止您的分隔符包含正則表達式解釋為特殊模式命令(如*( ) 並且因此必須轉義的字符)。

我想做一個像這樣的多行字符串但需要保留換行符所以我這樣做了

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM