让我们把表达式分解成几个部分:
^
从一行的开头开始。
(?:(2\d\d\d)\s+)?
如果出现年份,则将其捕获在第 1 组中(第 0 组是整个事件)。如果没有给出年份也没关系。
(?:Comm\. Rep\.\s+)?
接受字符串“Comm. Rep.” 和一个或多个空白字符,但不捕获它。如果它不存在也没关系。
(?:CONG\s+)?
接受字符串“CONG”和一个或多个空格字符,但不要捕获它。如果它不存在也没关系。
(\S+)\s+(\S+)\s+
必须有两个至少一个字符长度的非空白字符块,每个字符后跟至少一个空格。捕获第 2 组和第 3 组中的块。
(?:No\.\s+)?
接受字符串“否”。和一个或多个空白字符,但不捕获它。如果它不存在也没关系。
(\S+)
必须存在另一个至少有一个非空白字符的块。在第 4 组中捕获它。
(?:\s+\(.*?\))?
接受至少一个空格,然后是任何内容,只要它在括号内,但不要捕获它。如果它不存在也没关系。
$
然后该行必须结束。
以下是部分如何映射到当前匹配项(省略行首和行尾):
2009 IA H.B. 184 (NS)
---- ------- --- ----
2 5 7 8
您的问题在于表达式的第 5 部分,因为只接受两个“块”:
2009 IA HEART RATE 184 (NS)
---- ------------- --- ----
2 ??? 7 8
假设字符串“HEART RATE”也应该进入第三组,您需要将第 5 部分替换为:
(\S+)\s+
接受至少一个非空白字符块,后跟至少一个空白字符,并将其捕获到第 2 组(未更改)。
和
(.+?)\s+
接受任何内容的至少一个字符,后跟一个空格,并捕获除第 3 组中的最后一个空格之外的所有字符。
所以,这是你需要的整体表达:
^(?:(2\d\d\d)\s+)?(?:Comm\. Rep\.\s+)?(?:CONG\s+)?(\S+)\s+(.+?)\s+(?:No\.\s+)?(\S+)(?:\s+\(.*?\))?$
顺便说一句,我对http://regexpal.com非常满意。
PS:Carl Walsh 的解决方案在性能方面更聪明,因为它不依赖于非贪婪(或懒惰)捕获。