有没有办法,使用正则表达式来匹配引号之外的文本模式?

时间:2009-04-08 17:34:37

标签: regex

正如标题中所述,是否有一种方法,使用正则表达式来匹配出现在引号之外的文本的文本模式。理想情况下,给出以下示例,我希望能够匹配引号之外的逗号,但不能匹配引号中的逗号。

  

这是一些文字,后跟“文字,引号!”

  

这是一些文字,后跟“带引号的文字”,带有更多“文字,引号!”

此外,如果表达式遵循嵌套引号会很好,如下例所示。但是,如果这在技术上不适用于正则表达式,那么很容易知道是否是这种情况。

  

程序员从他的办公桌上抬起头,“这不可能很好,”他大声说道,“系统正在说'找不到文件!'”

我找到了一些用于匹配引号中某些内容的表达式,但对引号之外的内容没什么用。

9 个答案:

答案 0 :(得分:4)

最简单的是匹配逗号和引用的字符串,然后过滤掉引用的字符串。

/"[^"]*"|,/g

如果你真的不能匹配引号,你可以这样做:

/,(?=[^"]*(?:"[^"]*"[^"]*)*\Z)/g

这可能会变慢,因为对于每个逗号,它必须查看剩余的字符并计算引号的数量。 \Z匹配字符串的结尾。与$类似,但永远不会匹配行结束。

如果您不介意额外的捕获组,可以这样做:

/\G((?:[^"]*"[^"]*")*?[^"]*?)(,)/g

这只会扫描一次字符串。它从字符串的开头开始计算引号。 \G将匹配上次匹配结束的位置。


最后一个模式可能需要一个例子。

Input String: 'This is, some text, followed by "text, in quotes!" and more ,-as'
Matches:
1. ['This is', ',']
2. [' some text', ',']
3. [' and followed by "text, in quotes!" and more ', ',']

它匹配前导到逗号的字符串以及逗号。

答案 1 :(得分:2)

这可以通过现代正则表达式完成,因为存在大量的正则表达式引擎,但让我成为发布“不要使用正则表达式”答案的人。

这是正则表达式的作业。这是一个完整的解析器的工作。作为一些你不能用(经典)正则表达式做的事情的例子,请考虑这个:

()(())(()())

没有(经典)正则表达式可以确定这些括号是否匹配正确,但没有正则表达式这样做是微不足道的:

/* C code */

char string[] = "()(())(()())";
int parens = 0;
for(char *tmp = string; tmp; tmp++)
{
  if(*tmp == '(') parens++;
  if(*tmp == ')') parens--;
}
if(parens > 0)
{
  printf("%s too many open parenthesis.\n", parens);
}
else if(parens < 0)
{
  printf("%s too many closing parenthesis.\n", -parens);
}
else
{
  printf("Parenthesis match!\n");
}

# Perl code

my $string = "()(())(()())";
my $parens = 0;
for(split(//, $string)) {
  $parens++ if $_ eq "(";
  $parens-- if $_ eq ")";
}
die "Too many open parenthesis.\n" if $parens > 0;
die "Too many closing parenthesis.\n" if $parens < 0;
print "Parenthesis match!";

看一下编写一些非正则代码来为你完成这项工作有多简单?

编辑:好的,从看到探险世界回来了。 :)尝试这个(用Perl编写,评论是为了帮助你理解我在做什么,如果你不知道Perl):

# split $string into a list, split on the double quote character
my @temp = split(/"/, $string);

# iterate through a list of the number of elements in our list
for(0 .. $#temp) {

  # skip odd-numbered elements - only process $list[0], $list[2], etc.
  # the reason is that, if we split on "s, every other element is a string
  next if $_ & 1;

  if($temp[$_] =~ /regex/) {
    # do stuff
  }

}

另一种方法:

my $bool = 0;
my $str;
my $match;

# loop through the characters of a string
for(split(//, $string)) {

  if($_ eq '"') {
    $bool = !$bool;
    if($bool) {

      # regex time!
      $match += $str =~ /regex/;

      $str = "";
    }
  }

  if(!$bool) {

    # add the current character to our test string
    $str .= $_;
  }
}

# get trailing string match
$match += $str =~ /regex/;

(我给出了两个,因为在另一种语言中,一种解决方案可能比另一种解决方案更容易实现,而不仅仅是因为有多种方法可以做到这一点。)

当然,随着你的问题越来越复杂,构建一个完整的解析器会产生某些好处,但这是一个不同的马。现在,这就足够了。

答案 2 :(得分:1)

如前所述regexp cannot match any nested pattern,因为它不是Context-free language

因此,如果你有任何嵌套引号,你就不会用正则表达式解决这个问题 (除了。{regex引擎的“balancing group”功能之外 - 正如Daniel L在评论中所提到的那样 - 但我在这里没有假设正则表达式的风格)

除非您添加进一步的规范,否则必须转义报价中的报价。

在这种情况下,以下内容:

text before string "string with \escape quote \" still
within quote" text outside quote "within quote \" still inside" outside "
inside" final outside text

将成功匹配:

(?ms)((?:\\(?=")|[^"])+)(?:"((?:[^"]|(?<=\\)")+)(?<!\\)")?
  • group1:引用文字前面的文字
  • group2:双引号内的文字,即使其中包含\"

答案 3 :(得分:0)

这是一个获得匹配的表达式,但它并不完美,因为它获得的第一个匹配是整个字符串,删除最后的“。

[^"].*(,).*[^"]

我一直在使用我的Free RegEx tester来查看哪些有用。

测试结果

Group Match Collection # 1
Match # 1
Value: This is some text, followed by "text, in quotes!
Captures: 1

Match # 2
Value: ,
Captures: 1

答案 4 :(得分:0)

你最好自己构建一个简单的解析器(伪代码):

quoted := False
FOR char IN string DO
    IF char = '"'
        quoted := !quoted
    ELSE
        IF char = "," AND !quoted
            // not quoted comma found
        ENDIF
    ENDIF
ENDFOR

答案 5 :(得分:0)

这实际上取决于您是否允许嵌套引号。

理论上,使用嵌套引号你不能这样做(常规语言不能计算)

在实践中,您可以管理是否可以约束深度。随着您增加复杂性,它将变得越来越难看。这通常是人们如何通过正则表达式(试图匹配一般不常规的东西)来解决悲伤的方式。

请注意,某些“正则表达式”库/语言添加了非常规功能。

如果这种事情变得足够复杂,你真的必须为它编写/生成一个解析器。

答案 6 :(得分:0)

您的描述中需要更多内容。你想要任何可能的引用字符串和非引用的字符串吗...

Lorem ipsum“dolor sit”amet,“consectetur adipiscing”elit。

......或者只是你要求的模式?我认为这非常接近......

(?<outside>.*?)(?<inside>(?=\"))

然而它确实抓住了“。”

答案 7 :(得分:0)

也许你可以分两步完成? 首先,您替换引用的文本:

("[^"]*")

然后从剩余的字符串

中提取您想要的内容

答案 8 :(得分:0)

,(?=(?:[^"]*"[^"]*")*[^"]*\z)

正则表达式可能无法计算,但它们可以确定是否存在奇数或偶数。在找到一个逗号之后,前瞻断言,如果前面有任何引号,则会有偶数个引号,这意味着逗号在一组引号中

如果需要,可以调整这个来处理转义引号,尽管最初的问题没有提到。此外,如果你的正则表达式支持它们,我会添加原子组或占有量词来控制回溯。