拆分一个具有空格的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比尼翁,我只是增加了一个保障。