给定这段文字:
/* F004 (0309)00 */ /* field 1 */ /* field 2 */ /* F004 (0409)00 */ /* field 1 */ /* field 2 */
how do I parse it into this array:
[<br/>
["F004"],["0309"],["/* field 1 */\n/* field 2 */"],<br/>
["F004"],["0409"],["/* field 1 */\n/* field 2 */"]<br/>
]
I got code working to parse the first two items:
form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
text.scan(form)
[<br/>
["F004"],["0309"],<br/>
["F004"],["0409"]<br/>
]
下面是我尝试解析所有这三个代码并因无效的正则表达式错误而失败的代码:
form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
form_and_fields = /#{form}(.[^#{form}]+)/m
text.scan(form_and_fields)
编辑: 多亏了 rampion 和 singpolyma:
form = /
\/\*\s+(\w+)\s+\((\d+)\)\d+\s+\*\/ #formId & edDate
(.+?) #fieldText
(?=\/\*\s+\w+\s+\(\d+\)\d+\s+\*\/|\Z) #stop at beginning of next form
# or the end of the string
/mx
text.scan(form)
最佳答案
您似乎误解了字符类(例如 [a-f0-9]
或 [^aeiouy]
)的工作原理。 /[^abcd]/
不会否定模式 abcd
,它表示“匹配任何不是 'a'
或 的字符'b'
或 'c'
或 'd'
”。
如果要匹配模式的否定,请使用 /(?!pattern)/
结构。这是一个零宽度匹配 - 这意味着它实际上不匹配任何字符,它匹配一个位置。
类似于 /^/
和 /$/
匹配字符串的开始和结束,或者 /\b/
匹配字符串的边界单词。例如:/(?!xx)/
匹配模式“xx”未开始的每个位置。
那么一般来说,在使用模式否定之后,您需要匹配一些字符才能在字符串中向前移动。
所以要使用你的模式:
form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
form_and_fields = /#{form}((?:(?!#{form}).)+)/m
text.scan(form_and_fields)
由内而外(我将使用 (?#comments)
)
(?!#{form})
否定您的原始模式,因此它匹配您的原始模式无法开始的任何位置。(?:(?!#{form}).)+
表示在之后匹配一个字符,然后重试,尽可能多次,但至少一次。(?:(?#whatever))
是一个非捕获括号 - 适合分组。
在 irb 中,这给出:
irb> text.scan(form_and_fields)
=> [["F004", "0309", " \n /* field 1 */ \n /* field 2 */ \n ", nil, nil], ["F004", "0409", " \n /* field 1 */ \n /* field 2 */ \n", nil, nil]]
额外的 nil
来自 form
中的捕获组,用于否定模式 (?!#{form})
因此在成功匹配时不捕获任何内容。
这可以清理一些:
form_and_fields = /#{form}\s*(.+?)\s*(?:(?=#{form})|\Z)/m
text.scan(form_and_fields)
现在,我们使用零宽度正前瞻 (?=#{form})
来匹配下一次出现 form 的位置,而不是零宽度负前瞻
。所以在这个正则表达式中,我们匹配所有内容,直到 form
下一次出现(没有在我们的匹配中包括下一次出现)。这让我们可以修剪字段周围的一些空白。我们还必须检查我们到达字符串末尾的情况 - /\Z/
,因为这也可能发生。
在 irb 中:
irb> text.scan(form_and_fields)
=> [["F004", "0309", "/* field 1 */ \n /* field 2 */", "F004", "0409"], ["F004", "0409", "/* field 1 */ \n /* field 2 */", nil, nil]]
现在请注意,最后两个字段是第一次填充 - b/c 零宽度正前瞻中的捕获括号匹配了一些东西,即使它在这个过程中没有被标记为“已消耗” - 这就是为什么该位可以进行第二次重新匹配。
关于ruby - 在 ruby 中结合正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/652874/