拆分一个具有空格的string,除非它们被包含在“引号”中?

为了简单起见:

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

我希望能够将它们保存为两个不同的string,删除所有空格,除了在引号之间find的空格。 所以,我需要的是:

 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 

我看了正则expression式拆分空间除非在引号,但我似乎无法得到正则expression式工作/了解代码,特别是如何拆分它们,所以他们是两个不同的string。 所有的代码给我一个编译错误(我正在使用System.Text.RegularExpressions

 string input = "one \"two two\" three \"four four\" five six"; var parts = Regex.Matches(input, @"[\""].+?[\""]|[^ ]+") .Cast<Match>() .Select(m => m.Value) .ToList(); 

你甚至可以做到这一点没有正则expression式:与String.Split的LINQexpression式可以完成这项工作。

你可以先分割你的string,然后再用结果数组分割只有偶数索引的元素

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

对于string:

 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 

UPDATE

 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

你如何定义string的引用部分?

我们将假设第一个string之前的string是非引号。

然后,引用位于第一个"和第二个之前"之间的string。 第二个"和第三个"之间的string是不加引号的。 第三和第四之间的string被引用,…

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

什么是与String.Split的关系?

String.Split与默认的StringSplitOption(定义为StringSplitOption.None)创build1个string的列表,然后在列表中为每个find的分割字符添加一个新的string。

因此,在第一个"之前,string在分割数组中的索引0处,在第一个和第二个之间" ,该string在数组中的索引1处,在第三和第四索引2之间。

一般规则是:第n个和第(n + 1)个"之间的string在数组中的索引n处。 (2)

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

您可以使用属于Microsoft.VisualBasic.FileIO命名空间的TextFieldParser类。 (您需要将对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. 

由于自定义分析器可能更适合于此。

这是我写了一次,当我有一个具体的(和非常奇怪的)分析要求涉及括号和空格,但它是足够通用的,它应该几乎任何分隔符和文本限定符。

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

Squazz的答案只是一个小问题..它适用于他的string,但不是如果你添加更多的项目。 例如

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

我也把它变成一个扩展方法,并作出分隔符和转义字符可configuration。

OP想要

…删除所有空格,除了引号之间的空格

塞德里克·比尼翁(CédricBignon)的解决scheme几乎是这样做的,但没有考虑到可能会有不均一致的引号。 首先检查这一点,然后删除多余的,确保我们只停止拆分,如果元素真的用引号封装。

 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比尼翁,我只是增加了一个保障。