使用空格和引号解析字符串(保留引号)

7
如果我有一个像这样的字符串:
create myclass "56, 'for the better or worse', 54.781"

我该如何解析它,以便得到以下内容的三个字符串“单词”:
[0] create
[1] myclass
[2] "56, 'for the better or worse', 54.781"

编辑2:请注意引号需要保留

起初,我尝试使用string.Split(' '),但我发现它会将第三个string分成几个字符串。

我尝试通过在Split中使用count参数作为3来限制结果数量以解决这个问题。对于这种情况,这样做没问题,但当给出的字符串是:

create myclass false "56, 'for the better or worse', 54.781" //or
create myclass "56, 'for the better or worse', 54.781" false

由于最后两个单词会被合并,因此拆分失败。

我还创建了类似于ReadInBetweenSameDepth的东西,以获取引号之间的string

这是我的ReadInBetweenSameDepth方法

//Examples:
    //[1] (2 + 1) * (5 + 6) will return 2 + 1
    //[2] (2 * (5 + 6) + 1) will return 2 * (5 + 6) + 1
public static string ReadInBetweenSameDepth(string str, char delimiterStart, char delimiterEnd) {
  if (delimiterStart == delimiterEnd || string.IsNullOrWhiteSpace(str) || str.Length <= 2)
    return null;
  int delimiterStartFound = 0;
  int delimiterEndFound = 0;
  int posStart = -1;
  for (int i = 0; i < str.Length; ++i) {
    if (str[i] == delimiterStart) {
      if (i >= str.Length - 2) //delimiter start is found in any of the last two characters
        return null; //it means, there isn't anything in between the two
      if (delimiterStartFound == 0) //first time
        posStart = i + 1; //assign the starting position only the first time...
      delimiterStartFound++; //increase the number of delimiter start count to get the same depth
    }
    if (str[i] == delimiterEnd) {
      delimiterEndFound++;
      if (delimiterStartFound == delimiterEndFound && i - posStart > 0)
        return str.Substring(posStart, i - posStart); //only successful if both delimiters are found in the same depth
    }
  }
  return null;
}

虽然这个函数能够工作,但我发现将结果与string.Split结合起来以实现我想要的正确解析相当困难。

编辑2:在我的糟糕解决方案中,我需要稍后重新添加引号

有没有更好的方法来解决这个问题?如果我们使用Regex,该如何操作?

编辑:

我真的不知道这个问题可以像CSV格式的文本一样解决。我也不知道这个问题不一定要通过Regex来解决(因此我将其标记为这样)。对于那些认为这是重复帖子的人,我真诚地道歉。

编辑2:

在我的项目上工作了更多之后,我意识到我的问题有些问题(即,我没有包括引号)- 向先前最佳答案者Tim Schmelter先生道歉。然后,在查看重复链接后,我注意到它也没有提供解决方案。

3个回答

3
你可以通过这个进行分割。
\s(?=(?:[^"]*"[^"]*")*[^"]*$)

See demo.

https://regex101.com/r/fM9lY3/60

string strRegex = @"\s(?=(?:[^""]*""[^""]*"")*[^""]*$)";
Regex myRegex = new Regex(strRegex, RegexOptions.Multiline);
string strTargetString = @"create myclass ""56, 'for the better or worse', 54.781""";

return myRegex.Split(strTargetString);

谢谢,我认为这是最好的答案,因为我正在使用C#完成这个任务。但是,我并不知道我的问题与CSV解析相同。 - Ian
1
哦,拜托,这是这里最糟糕的答案!如果可以不用正则表达式,请不要使用它!看看它需要多少回溯。我宁愿选择一个更详细解释的答案。 - Wiktor Stribiżew
@stribizhev 先生,你有更好的答案吗?因为我的方法显然比所有给出的答案都要糟糕。 - Ian
答案在重复的问题中,或者在这里:`public static string[] parse(string csv, string separator) { TextFieldParser parser = new TextFieldParser(new StringReader(csv)); parser.HasFieldsEnclosedInQuotes = true; parser.SetDelimiters(separator); string[] fields = null; while (!parser.EndOfData) fields = parser.ReadFields(); parser.Close(); return fields; }`。将空格设置为分隔符,就这样,很安全。添加 `using Microsoft.VisualBasic.FileIO;` 和 `System.IO;`。 - Wiktor Stribiżew
@stribizhev 所以你也认为 TextFieldParser 是像 TimSchmelter 先生说的那样最好的方法。谢谢,我会去查看一下的。 - Ian
其实,Tushar对他的正则表达式给出了更详细的解释 :) - Wiktor Stribiżew

2

正则表达式演示

(\w+|"[^"]*")

获取第一组捕获的匹配项。

  1. \w+: 匹配一个或多个字母数字字符和下划线
  2. "[^"]*": 匹配被双引号包含的任何内容
  3. |: 正则表达式中的OR条件

谢谢,我测试了正则表达式并且它运行良好。也感谢您的解释。 - Ian
谢谢Tushar先生,经过进一步处理数据,看起来您的解决方案是最好的,而且还有解释。这是我另一个帖子,让我这么想:http://stackoverflow.com/questions/34624536/stringsplitoptions-removeemptyentries-equivalent-for-textfieldparser - Ian

1
我会为这个任务使用一个真正的CSV解析器。框架中唯一可用的是VisualBasic命名空间中的TextFieldParser
string str = "create myclass \"56, 'for the better or worse', 54.781\"";
var allLineFields = new List<string[]>();
using (var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(new StringReader(str)))
{
    parser.Delimiters = new string[] { " " };
    parser.HasFieldsEnclosedInQuotes = true;  // important
    string[] lineFields;
    while ((lineFields = parser.ReadFields()) != null)
    {
        allLineFields.Add(lineFields);
    }
}

结果:

enter image description here

但是还有其他可用的选项,比如thisthis


谢谢,我不知道VB库中有TextFieldParser可以这样使用。感谢您的建议。+10 - Ian
@Ian:如果你要解析一些较大的文本,它比使用正则表达式更有效,你可以毫无问题地在 C# 中使用它。不用客气。 - Tim Schmelter
哦,我明白了...我的错。显然,既然它被转换为dll,那么它应该是.Net类,而不是VB,因此在C#中也可以很容易地使用它。感谢您的纠正。我也想看看它的性能。 - Ian
@TimSchmelter:在我之前很久你就应该将这个问题标记为重复了。你已经回答过这样的问题一百万次了。 - Wiktor Stribiżew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接