54

为了简单起见:

string streamR = sr.ReadLine();  // sr.Readline results in:
                                 //                         one "two two"

我希望能够将它们保存为两个不同的字符串,删除所有空格,除了引号之间的空格。因此,我需要的是:

string 1 = one
string 2 = two two

到目前为止,我发现有效的是以下代码,但它删除了引号内的空格。

//streamR.ReadLine only has two strings
  string[] splitter = streamR.Split(' ');
    str1 = splitter[0];
    // Only set str2 if the length is >1
    str2 = splitter.Length > 1 ? splitter[1] : string.Empty;

这个的输出变成

one
two

我已经研究了正则表达式以拆分空格,除非在引号中但是我似乎无法让正则表达式工作/理解代码,特别是如何拆分它们,使它们成为两个不同的字符串。那里的所有代码都给我一个编译错误(我正在使用System.Text.RegularExpressions

4

7 回答 7

59
string input = "one \"two two\" three \"four four\" five six";
var parts = Regex.Matches(input, @"[\""].+?[\""]|[^ ]+")
                .Cast<Match>()
                .Select(m => m.Value)
                .ToList();
于 2013-02-01T21:15:17.670 回答
39

你甚至可以在没有 Regex 的情况下做到这一点:一个 LINQ 表达式String.Split可以完成这项工作。

您可以在之前拆分字符串,"然后仅将结果数组中索引为偶数的元素拆分为 .

var result = myString.Split('"')
                     .Select((element, index) => index % 2 == 0  // If even index
                                           ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                           : new string[] { element })  // Keep the entire item
                     .SelectMany(element => element).ToList();

对于字符串:

This is a test for "Splitting a string" that has white spaces, unless they are "enclosed within quotes"

它给出了结果:

This
is
a
test
for
Splitting a string
that
has
white
spaces,
unless
they
are
enclosed within quotes

更新

string myString = "WordOne \"Word Two\"";
var result = myString.Split('"')
                     .Select((element, index) => index % 2 == 0  // If even index
                                           ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                           : new string[] { element })  // Keep the entire item
                     .SelectMany(element => element).ToList();

Console.WriteLine(result[0]);
Console.WriteLine(result[1]);
Console.ReadKey();

更新 2

你如何定义字符串的引号部分?

我们将假设第一个字符串之前的字符串"没有被引用。

然后,放在第一个"和第二个之前的字符串"被引用。第二个"和第三个之间的字符串"不加引号。第三个和第四个之间的字符串被引用,...

一般规则是:第 (2*n-1) 个(奇数)"和第 (2*n) 个(偶数)之间的每个字符串"都被引用。(1)

有什么关系String.Split

String.Split 使用默认的 StringSplitOption(定义为 StringSplitOption.None)创建一个包含 1 个字符串的列表,然后在列表中为找到的每个拆分字符添加一个新字符串。

因此,在 first 之前",字符串位于拆分数组中的索引 0 处,介于 first 和 second 之间",字符串位于数组中的索引 1 处,位于第三个和第四个之间,索引 2,...

一般规则是:第 n 个和第 (n+1) 个之间的字符串位于"数组中的索引 n 处。(2)

给定(1)and (2),我们可以得出结论: 引用部分在拆分数组中的奇数索引处。

于 2013-02-01T21:18:55.703 回答
14

因为自定义解析器可能更适合这个。

这是我曾经写过的东西,当时我有一个涉及括号和空格的特定(并且非常奇怪)的解析要求,但它足够通用,几乎可以与任何分隔符和文本限定符一起使用。

public static IEnumerable<String> ParseText(String line, Char delimiter, Char textQualifier)
{

    if (line == null)
        yield break;

    else
    {
        Char prevChar = '\0';
        Char nextChar = '\0';
        Char currentChar = '\0';

        Boolean inString = false;

        StringBuilder token = new StringBuilder();

        for (int i = 0; i < line.Length; i++)
        {
            currentChar = line[i];

            if (i > 0)
                prevChar = line[i - 1];
            else
                prevChar = '\0';

            if (i + 1 < line.Length)
                nextChar = line[i + 1];
            else
                nextChar = '\0';

            if (currentChar == textQualifier && (prevChar == '\0' || prevChar == delimiter) && !inString)
            {
                inString = true;
                continue;
            }

            if (currentChar == textQualifier && (nextChar == '\0' || nextChar == delimiter) && inString)
            {
                inString = false;
                continue;
            }

            if (currentChar == delimiter && !inString)
            {
                yield return token.ToString();
                token = token.Remove(0, token.Length);
                continue;
            }

            token = token.Append(currentChar);

        }

        yield return token.ToString();

    } 
}

用法是:

var parsedText = ParseText(streamR, ' ', '"');
于 2013-02-01T21:17:36.097 回答
12

您可以使用属于命名空间的TextFieldParserMicrosoft.VisualBasic.FileIO。(您需要添加对Microsoft.VisualBasic项目的引用。):

string inputString = "This is \"a test\" of the parser.";

using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(inputString)))
{
    using (Microsoft.VisualBasic.FileIO.TextFieldParser tfp = new TextFieldParser(ms))
    {
        tfp.Delimiters = new string[] { " " };
        tfp.HasFieldsEnclosedInQuotes = true;
        string[] output = tfp.ReadFields();

        for (int i = 0; i < output.Length; i++)
        {
            Console.WriteLine("{0}:{1}", i, output[i]);
        }
    }
}

生成输出:

0:This
1:is
2:a test
3:of
4:the
5:parser.
于 2013-02-01T21:18:58.807 回答
2

支持双引号。

细绳:

a "b b" "c ""c"" c"

结果:

a 
"b b"
"c ""c"" c"

代码:

var list=Regex.Matches(value, @"\""(\""\""|[^\""])+\""|[^ ]+", 
    RegexOptions.ExplicitCapture)
            .Cast<Match>()
            .Select(m => m.Value)
            .ToList();

可选删除双引号:

Select(m => m.StartsWith("\"") ? m.Substring(1, m.Length - 2).Replace("\"\"", "\"") : m)

结果

a 
b b
c "c" c
于 2018-02-11T11:06:01.820 回答
1

Squazz 的回答只是一个小问题.. 它适用于他的字符串,但如果你添加更多项目则不行。例如

string myString = "WordOne \"Word Two\" Three"

在这种情况下,删除最后一个引号会得到 4 个结果,而不是 3 个。

不过,这很容易解决..只需计算转义字符的数量,如果不均匀,则去掉最后一个(根据您的要求进行调整..)

    public static List<String> Split(this string myString, char separator, char escapeCharacter)
    {
        int nbEscapeCharactoers = myString.Count(c => c == escapeCharacter);
        if (nbEscapeCharactoers % 2 != 0) // uneven number of escape characters
        {
            int lastIndex = myString.LastIndexOf("" + escapeCharacter, StringComparison.Ordinal);
            myString = myString.Remove(lastIndex, 1); // remove the last escape character
        }
        var result = myString.Split(escapeCharacter)
                             .Select((element, index) => index % 2 == 0  // If even index
                                                   ? element.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                                   : new string[] { element })  // Keep the entire item
                             .SelectMany(element => element).ToList();
        return result;
    }

我还将它变成了一种扩展方法,并使分隔符和转义符可配置。

于 2017-05-16T13:34:15.757 回答
0

OP想要

...删除所有空格,引号之间的空格除外

Cédric Bignon 的解决方案几乎做到了这一点,但没有考虑到引号可能是奇数。首先检查这一点,然后删除多余的,确保我们只有在元素真的被引号封装时才停止拆分。

string myString = "WordOne \"Word Two";
int placement = myString.LastIndexOf("\"", StringComparison.Ordinal);
if (placement >= 0)
myString = myString.Remove(placement, 1);

var result = myString.Split('"')
                     .Select((element, index) => index % 2 == 0  // If even index
                                           ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                           : new string[] { element })  // Keep the entire item
                     .SelectMany(element => element).ToList();

Console.WriteLine(result[0]);
Console.WriteLine(result[1]);
Console.ReadKey();

逻辑归功于 Cédric Bignon,我只添加了一个保护措施。

于 2016-02-16T09:25:20.013 回答