0

我已经看到了很多非常接近我需要的正则表达式答案,但它并不完全存在。问题是我有一个字符串需要在字符上拆分(例如:空格或'='),但我想忽略引号内的任何内容(甚至引号内的引号)。

我能得到的最接近的

" (?=(?:[^"]*"[^"]*")*[^"]*$)"

效果很好,有两个警告:引号中的时间间隔不正确会触发错误的拆分,并且会向后读取。我并不真正关心的第一个问题,我无能为力,我可以解决它。但第二个很关键。

情况是,有时我正在正则表达式的字符串可能会不小心遗漏最后的引号。这并没有真正打扰我的系统,但上面的正则表达式倒退了,所以它破坏了一切:

string test = "foo bar \"foo bar\" foobar \"foo"
var result = Regex.Split(test, " (?=(?:[^"]*"[^"]*")*[^"]*$)");

这将使:

foo bar "foo
bar" foobar "foo

因为它从最后开始并向后运行过滤器。我需要的结果是:

foo
bar
"foo bar"
foobar
"foo

我知道 $ 负责从头到尾的事情,但我无法为我的生活弄清楚如何扭转它。想法?

4

4 回答 4

1

它实际上并没有向后运行,只是每次应用前瞻时都必须一直匹配到最后。这是唯一可以确保在当前位置后面有偶数个引号的方法。

但这无论如何都是一个骇人听闻的解决方案。只有在被迫使用Split(). 匹配令牌本身通常要容易得多。例如:

string s = @"foo bar ""foo bar"" foobar ""foo";
Regex r = new Regex(@"[^""\s]+|""[^""]+(?:""|$)");

foreach (Match m in r.Matches(s))
{
  Console.WriteLine(m.Value);
}

输出:

foo
bar
"foo bar"
foobar
"foo

编辑: 此版本允许不带引号的标记包含引号:

@"[^""\s]\S+|""[^""]+(?:""|$)"

我仍然假设未引用的标记不能包含任何空格。


编辑: 似乎引号一直都很特别,而不仅仅是当它们是标记中的第一个非空白字符时。在此版本中,标记可以以非引号开头或结尾,并且可以包含一个或多个带引号的序列。因为一切都是可选的,所以它以防止它匹配空字符串的前瞻开始。

@"(?=\S)[^\s""]*(?:""[^""]+(?:$|""[^\s""]*))*"

和以前一样,最后的结束报价是可选的。

于 2013-10-30T02:03:32.327 回答
1

您可以在拆分时使用此正则表达式。

("[^"]+"|\s+)

如果您将模式括在括号内,大多数拆分函数将返回使用的分隔符。在这种情况下,您首先尝试在当前位置匹配带引号的单词,如果无法匹配,则选择匹配空格。

获得所有值后,只需删除那些仅包含您要丢弃的分隔符(在本例中为空格)的值。

这是一个使用 Perl 的示例。

use warnings;
use strict;

my $string = "foo bar \"foo bar\" foobar \"foo";

my @array =  grep { ! /^\s*$/ } # Discard matches containing only spaces.
                 split /("[^"]+"|\s+)/, $string; # Split on whitespace or character withing quotes
                                         # Return delimiters as part of the match.    

print "$_\n" foreach @array;

输出

foo
bar
"foo bar"
foobar
"foo
于 2013-10-29T19:05:46.337 回答
0

我认为 Regex 1 或 Regex 2 应该可以解决问题。

 # =====================================
 # Regex 1
 # =====================================
 #    ("[^"]")|[\s=]+             // raw
 #    "(\"[^\"]\")|[\\s=]+"       // escped
 #    @"                          // verbatim
 #     (""[^""]"")|[\s=]+
 #    "
 # -------------------------------------
 #    
 #         ( " [^"] " )      # expanded Regex 1
 #      |  
 #         [\s=]+ 

 # =====================================
 # Regex 2
 # =====================================
 #    ("(?:[^"]*"[^"]*")*[^"]*")|[\s=]+             // raw
 #    "(\"(?:[^\"]*\"[^\"]*\")*[^\"]*\")|[\\s=]+"   // escaped
 #    @"                                            // verbatim
 #     (""(?:[^""]*""[^""]*"")*[^""]*"")|[\s=]+
 #    "
 # -------------------------------------
 #        
 #        (                  # expanded Regex 2
 #             " 
 #             (?: [^"]* " [^"]* " )*
 #             [^"]* 
 #             "     
 #        )
 #     |  
 #        [\s=]+ 
于 2013-10-29T19:53:17.830 回答
0

如果您尝试这种方法怎么办

string test = "foo bar \"foo bar\" foobar \"foo";
if (test.Count(q => q == '"')%2 == 1)
    test += "\"";

test = Regex.Replace(test, "\"[^\"]+\"", "");

测试它是否有奇数个引号,如果有,则添加一个。然后使用删除引号内的任何内容"\"[^\"]+\""。然后你可以简单地使用 String.Split() 来分割它

于 2013-10-29T17:42:49.703 回答