正则表达式拆分空间没有引号向前阅读

本文关键字:拆分 空间 正则表达式 | 更新日期: 2023-09-27 18:37:16

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

我能得到的最接近的是这个:

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

这很好用,但有两个警告:引号中不合时宜的空格会触发错误的拆分,并且它会向后读取。 第一个问题我并不真正关心,我能做的不多,我可以解决它。 但第二个至关重要。

情况是,有时我正在正则表达式的字符串可能会意外地在末尾缺少引号。 这并没有真正打扰我的系统,但是上面的正则表达式倒退了,所以它破坏了一切:

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

我知道 $ 负责结束事情的开始,但我一生都无法弄清楚如何逆转它。 思潮?

正则表达式拆分空间没有引号向前阅读

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

但无论如何,这是一个黑客解决方案; 只有当你被迫使用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""]*))*"

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

您可以在

拆分时使用此正则表达式。

("[^"]+"|'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

如果你尝试这种方法会怎样

string test = "foo bar '"foo bar'" foobar '"foo";
if (test.Count(q => q == '"')%2 == 1)
    test += "'"";
test = Regex.Replace(test, "'"[^'"]+'"", "");

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

我认为正则表达式 1 或正则表达式 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=]+