c#正则表达式列表解析

问题描述:

我有一个文本字段,接受用户输入的字符串的形式列表的形式。我有两个主要的分隔符,一个空格和一个逗号。c#正则表达式列表解析

如果列表中的项目包含多个单词,用户可以通过将其用引号引起来对其进行定制。

样品输入:

Apple, Banana Cat, "Dog starts with a D" Elephant Fox "G is tough", "House" 

所需的输出:

Apple 
Banana 
Cat 
Dog starts with a D 
Elephant 
Fox 
G is a tough one 
House 

我一直努力让一个正则表达式这一点,我无法弄清楚如何让逗号。以下是我迄今为止:

Regex.Matches(input, @"(?<match>\w+)|\""(?<match>[\w\s]*)""") 
      .Cast<Match>() 
      .Select(m => m.Groups["match"].Value.Replace("\"", "")) 
      .Where(x => x != "") 
      .Distinct() 
      .ToList() 

这正则表达式是相当聪明的,如果它可以把"G is tough"G is a tough one :-)

在一个更严重的是,码了一个解析器和不要试图依靠一个奇异的正则表达式来为你做这件事。

你会发现你了解更多,代码将更具可读性,而且你不会有,你甚至还没有想通了边缘的情况下担心自己还没有,如:

Apple, Banana Cat, "Dog, not elephant, starts with a D" Elephant Fox 

针对这种情况一个简单的解析器是:

state = whitespace 
word = "" 
for each character in (string + " "): 
    if state is whitespace: 
     if character is not whitespace: 
      word = character 
      state = inword 
    else: 
     if character is whitespace: 
      process word 
      word = "" 
      state = whitespace 
     else: 
      word = word + character 

,它是相对容易的添加对引用的支持:

state = whitespace 
quote = no 
word = "" 
for each character in (string + " "): 
    if state is whitespace: 
     if character is not whitespace: 
      word = character 
      state = inword 
    else: 
     if character is whitespace and quote is no: 
      process word 
      word = "" 
      state = whitespace 
     else: 
      if character is quote: 
       quote = not quote 
      else: 
       word = word + character 

请注意,我没有对这些进行彻底的测试,但是我在过去做了很多,所以我很自信。这只是一个很短的步骤,也可以允许转义(例如,如果您想要在引号内使用引号(如"The \" character is inside"))。

要获得能够处理多个分离的单个正则表达式是不是努力,得到它的监控状态,比如当你在引号内,这样你就可以区别对待分离,是另一个层次。

+0

感谢您的支持。我基本上希望不必写一个解析器。我肯定认为你是正确的,但需要做到这一点。看起来像很好的伪代码。我非常擅长编写解析器,我只是希望能够使用正则表达式。再次感谢。 – Mark 2010-10-26 04:29:48

+0

@Mark,我会认真考虑使用正则表达式来获得下一个项目,然后按照这个数量减少项目列表,例如:(1)去掉'^ [,] *',如果字符串空; (2)如果下一个字符是''',得到'^“[^”] *“'并且移除''''然后去掉那个长度并返回1;(3)得到'^ [^,] * [,]',删除结尾字符,去掉这个长度并回到1。这可能会大大简化解析器。 – paxdiablo 2010-10-26 10:50:57

您应该选择使用空格还是逗号作为分隔符。使用两者都有点令人困惑。如果这个选择不是你的选择,我会首先在引号之间抓取东西。当它们消失时,您可以用空格替换所有逗号并将空行分割。

你可以执行两个正则表达式。第一个匹配引用的部分,然后删除它们。有了第二个正则表达式,你可以匹配剩余的单词。

string pat = "\"(.*?)\"", pat2 = "(\\w+)"; 
string x = "Apple, Banana Cat, \"Dog starts with a D\" Elephant Fox \"G is tough\", \"House\""; 

IEnumerable<Match> combined = Regex.Matches(Regex.Replace(x, pat, ""), pat2).OfType<Match>().Union(Regex.Matches(x, pat).OfType<Match>()).Where(m => m.Success); 

foreach (Match m in combined) 
    Console.WriteLine(m.Groups[1].ToString()); 

让我知道如果这不是你在找什么。

+0

喜欢简单,但顺序搞砸了,我认为这是对这样的事情的要求。 – 2010-10-26 09:50:27

我喜欢paxdiablo的解析器,但是如果您想使用单个正则表达式,那么请考虑我的修改版本CSV regex parser

第1步:原

string regex = "((?<field>[^\",\\r\\n]+)|\"(?<field>([^\"]|\"\")+)\")(,|(?<rowbreak>\\r\\n|\\n|$))"; 

第2步:使用多个分隔符

char quoter = '"';  // quotation mark 
string delimiter = " ,"; // either space or comma 
string regex = string.Format("((?<field>[^\\r\\n{1}{0}]*)|[{1}](?<field>([^{1}]|[{1}][{1}])*)[{1}])([{0}]|(?<rowbreak>\\r\\n|\\n|$))", delimiter, quoter); 

使用一个简单的循环测试:

Regex re = new Regex(regex); 
foreach (Match m in re.Matches(input)) 
{ 
    string field = m.Result("${field}").Replace("\"\"", "\"").Trim(); 
    // string rowbreak = m.Result("${rowbreak}"); 
    if (field != string.Empty) 
    { 
     // Print(field); 
    } 
} 

我们得到的输出:

Apple 
Banana 
Cat 
Dog starts with a D 
Elephant 
Fox 
G is tough 
House 

就是这样!

查看原始CSV regex parser,了解如何处理匹配的正则表达式数据。你可能需要稍微修改它,但你会明白。

只是为了感兴趣,如果你足够疯狂,想要使用多个字符作为单个分隔符,则考虑this answer