正则表达式匹配空格,但不是“字符串”

时间:2009-08-21 07:27:37

标签: c++ c regex qt

我正在寻找一个常规的exression匹配空格,只有当thos空格没有用双引号(“)括起来时。例如,在

Mary had "a little lamb"

它应匹配第一个和第二个空格,而不是其他空格。

我想将字符串拆分为不在双引号中的空格,而不是在引号处。

我正在使用C ++和Qt工具包,并希望使用QString :: split(QRegExp)。 QString与std :: string非常相似,而QRegExp基本上是封装在类中的POSIX正则表达式。如果存在这样的正则表达式,则拆分将是微不足道的。

示例:

Mary had "a little lamb"     =>   Mary,had,"a little lamb"
1" 2 "3                      =>   1" 2 "3 (no splitting at ")
abc def="g h i" "j k" = 12   =>   abc,def="g h i","j k",=,12

对于编辑很抱歉,当我首先提出问题时,我非常不精确。希望现在更清楚一点。

5 个答案:

答案 0 :(得分:7)

(我知道你刚刚发布了几乎完全相同的答案,但我不忍心把这一切都丢掉。: - /)

如果可以通过正则表达式拆分操作解决您的问题,正则表达式将必须匹配偶数个引号,正如MSalters所说。但是,拆分正则表达式应仅匹配您正在拆分的空间,因此其余工作必须在前瞻中完成。这是我要用的:

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

如果文本格式正确,则偶数引号的前瞻足以确定刚刚匹配的空间不在引用序列中。也就是说,lookbehinds不是必需的,这很好,因为QRegExp似乎不支持它们。转义报价也可以适应,但正则表达式变得更大,更丑陋。但是,如果您无法确定文本是否格式正确,那么您很可能无法使用split()来解决问题。

顺便说一句,QRegExp 实现POSIX regular expressions - 如果确实如此,它将不支持前瞻或后观。相反,它属于松散定义的Perl兼容正则表达式类别。

答案 1 :(得分:4)

"a" b "c"会发生什么?

请注意,在子字符串" b "中,空格位于引号之间。

- 编辑 -

我假设一个空格是“在引号之间”,如果它前面跟着奇数个标准引号(即U + 0022,我将忽略那些有趣的Unicode“引号”)。

这意味着您需要以下正则表达式:^[^"]*("[^"]*"[^"]*)*"[^"]* [^"]*"[^"]*("[^"]*"[^"]*)*$

("[^"]*"[^"]*)代表一对引号。 ("[^"]*"[^"]*)*是偶数引号,("[^"]"[^"]*)*"是奇数。然后是实际引用的字符串部分,后面是另一个奇数引号。需要^$个锚点,因为您需要计算字符串开头的每个引号。这通过从不查看子字符串来回答上面的" b "子字符串问题。价格是输入中的每个字符必须与整个字符串匹配,这会将其转换为O(N * N)拆分操作。

你可以在正则表达式中执行此操作的原因是因为需要有限量的内存。实际上只有一点; “到目前为止,我看到过奇数或偶数的报价吗?”您实际上不必匹配单个""对。

但这不是唯一可能的解释。如果您确实包含应配对的“funny Unicode quotes”,则还需要处理““double quoted””字符串。这反过来意味着你需要一个开放的计数,这意味着你需要无限存储,这反过来意味着它不再是常规语言,这意味着你不能使用正则表达式。 QED。

无论如何,即使有可能,你仍然需要一个合适的解析器。用于计算每个字符前面的引号数的O(N * N)行为并不好笑。如果你已经知道在Str [N]之前有X引号,那么它应该是一个O(1)操作来确定在Str [N + 1]之前有多少引号,而不是O(N)。可能的答案毕竟只是X或X + 1!

答案 2 :(得分:4)

MSalters推动我走上正轨。他回答的问题是他给出的正则表达式总是匹配整个字符串,因此不适合split(),但这可以部分地通过先行匹配来兑换。假设引号总是配对(确实是这样),我可以在每个空格上拆分,然后是偶数个引号。

没有C转义且单引号的正则表达式看起来像

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

在源代码中它最终看起来像(使用Qt和C ++)

QString buf("Mary had \"a little lamb\""); // string we want to split
QStringList splitted = buf.split( QRegExp(" (?=[^\"]*(\"[^\"]*\"[^\"]*)*$)") );

简单,嗯?

对于性能,字符串在程序开始时被解析一次,它们是几十个并且它们不到100个字符。我将用长字符串测试它的运行时间,以确保没有任何不好的事情发生; - )

答案 3 :(得分:1)

如果字符串中的引用很简单(如示例所示),则可以使用交替。这个正则表达式首先寻找一个简单的引用字符串;没有找到空格。

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

在Perl中,如果在调用split()时在正则表达式中使用分组,则该函数不仅返回元素,还返回捕获的组(在本例中为我们的分隔符)。如果您然后过滤掉空白和仅空格分隔符,您将获得所需的元素列表。我不知道类似的策略是否适用于C ++,但以下Perl代码确实有效:

use strict;
use warnings;
while (<DATA>){
    chomp;
    my @elements = split /(\"[^\"]*\"| +)/, $_;
    @elements = grep {length and /[^ ]/} @elements;
    # Do stuff with @elements
}

__DATA__
Mary had "a little lamb"
1" 2 "3
abc def="g h i" "j k" = 12

答案 4 :(得分:-2)

最简单的正则表达式解决方案:匹配整个空格和引号。稍后过滤报价

"[^"]*"|\s