2012-03-10 57 views
2

我使用这个正则表达式解析一个CSV线在APEX:如何仅为每个匹配返回非空捕获组?

Pattern csvPattern = Pattern.compile('(?:^|,)(?:\"([^\"]+|\"\")*\"|([^,]+)*)'); 

它的伟大工程,但返回两组,每场比赛(一个用于引用的值,以及一个用于非引用的值) 。请看下图:

Matcher csvMatcher = csvPattern.matcher('"hello",world'); 
Integer m = 1; 
while (csvMatcher.find()) { 
    System.debug('Match ' + m); 
    for (Integer i = 1; i <= csvMatcher.groupCount(); i++) { 
     System.debug('Capture group ' + i + ': ' + csvMatcher.group(i)); 
    } 
    m++; 
} 

运行这段代码将返回如下:

[5]|DEBUG|Match 1 
[7]|DEBUG|Capture group 1: hello 
[7]|DEBUG|Capture group 2: null 
[5]|DEBUG|Match 2 
[7]|DEBUG|Capture group 1: null 
[7]|DEBUG|Capture group 2: world 

我想每场比赛只返回非空捕获。那可能吗?

+2

你的正则表达式是有点坏了;注意类似'(a)*'的东西只会捕获匹配的'a'中的一个。所以你需要把'([^ \“] + | \”\“)*'改为'((?:[^ \”] + | \“\”)*)'如果你想捕捉整个内容一个包含'“”'的双引号字符串。 (还有一些其他问题;你的正则表达式不是写得非常防守的恕我直言。)另外 - 你似乎有点想*两个变种出现在单独的捕获组,不是吗? ?因为引用的变体需要后处理才能将''''''转换为''',而未加引号的变体不会。 – ruakh 2012-03-10 17:25:44

+0

我问过因为我想要你的NSHO!感谢你的NSHO!我刚开始这个,所以他们 – barelyknown 2012-03-10 18:39:44

回答

3

这实际上是一件很难做的事情。
它可以用前瞻/后面的断言来完成。
虽然不是很直观。

它看起来是这样的:
(?:^|,)(\s*"(?=(?:[^"]+|"")*"\s*(?:,|$)))?((?<=")(?:[^"]+|"")*(?="\s*(?:,|$))|[^,]*)

工作原理是在一个有效报价领域的第一次报价后"排队文本正文。如果它不是一个有效的引用字段,它就会在引用本身上排列。此时,可以在单个捕获缓冲区中将文本正文捕获为未加引号的字段或引号字段减去引号。

这可能是一个功率正则表达式,它可以在不需要残余代码的情况下提供精确的解决方案。我可能会错过一些东西,但是我没有办法在没有环视断言的情况下做到这一点。所以,你的引擎必须支持。如果没有,你必须像上面的解决方案一样挑选它。

这是Perl中的一个原型,在它下面有一个带注释的扩展正则表达式。
祝你好运!

$samp = ' "hello " , world",,me,and,th""is, or , "tha""t" '; 

$regex = ' 
    (?:^| ,) 
    (\s*" (?= (?:[^"]+|"")* " \s*(?:,|$)))? 
    (
    (?<=") (?:[^"]+|"")* (?="\s*(?:,|$)) 
    | 
    [^,]* 
) 
'; 
while ($samp =~ /$regex/xg) 
{ 
    print "'$2'\n"; 
} 

输出

'hello ' 
' world"' 
'' 
'me' 
'and' 
'th""is' 
' or ' 
'tha""t' 

评论

(?:^| ,)   # Consume comma (or BOL is fine) 

(     # Capture group 1, capture '"' only if a complete quoted field 
    \s*     # Optional many spaces 
    " 
    (?=     # Lookahead, check for a valid quoted field, determines if a '"' will be consumed 
     (?:[^"]+|"")* 
     " 
     \s* 
     (?:,|$) 
    ) 
)?     # End capt grp 1. 0 or 1 quote 

(     # Capture group 2, the body of text 
    (?<=")     # If there is a '"' behind us, we have consumed a '"' in capture grp 1, so this is valid 
    (?:[^"]+|"")* 
    (?="\s*(?:,|$)) 
|      # OR, 
    [^,]*     # Just get up to the next ',' This could be incomplete quoted fields 
)     # End capt grp 2 

扩展

事实上,如果你可以利用这一点,它可以加速消耗反向引用引述场
而不是匹配两次引用的字段。反向引用通常在C语言中解析为单个字符串
比较api,例如strncmp(),使其更快。
作为一个方面说明,可以在正则表达式中修改
并附带一些额外的符号。
祝你好运!

压缩

(?:^|,)(?:\s*"(?=((?:[^"]+|"")*)"\s*(?:,|$)))?((?<=")\1|[^,]*)

扩展

(?: ^|,) 
(?: \s* " (?= ((?:[^"]+|"")*) " \s* (?: ,|$) ))? 
((?<=") \1 | [^,]*) 

扩大了与评论

(?:^| ,)   # Consume comma (or BOL is fine) 

(?:     # Start grouping 
    \s*     # Spaces, then double quote '"' (consumed if valid quoted field) 
    "     # 
    (?=     # Lookahead, nothing consumed (check for valid quoted field) 
     (     # Capture grp 1 
     (?:[^"]+|"")*   # Body of quoted field (stored for later consumption) 
    )      # End capt grp 1 
     "      # Double quote '"' 
     \s*     # Optional spaces 
     (?: , | $)   # Comma or EOL 
    )     # End lookahead 
)?     # End grouping, optionaly matches and consumes '\s*"' 

(     # Capture group 2, consume FIELD BODY 
    (?<=")     # Lookbehind, if there is a '"' behind us the field is quoted 
    \1      # Consume capt grp 1 
|      # OR, 
    [^,]*     # Invalid-quoted or Non-quoted field, get up to the next ',' 
)     # End capt grp 2 
+0

哇,我从这个答案中学到了很多东西,它运行得很好,我同意这不是太直观,但是当我被困在将来时,我会在代码中链接到这个答案。 – barelyknown 2012-03-11 13:31:32

0

从ruakh的一些灵感中,我更新了正则表达式,每个匹配只返回一个捕获组(并处理字段和空格内的引号)。

(?:^|[\s]*?,[\s]*)(\"(?:(?:[^\"]+|\"\")*)[^,]*|(?:[^,])*) 
相关问题