Set*_*eno 1 ruby regex parsing text
鉴于此文本:
/* F004 (0309)00 */
/* field 1 */
/* field 2 */
/* F004 (0409)00 */
/* field 1 */
/* field 2 */
我如何解析它到这个数组:
[
["F004"],["0309"],["/* field 1 */\n/* field 2 */"],
["F004"],["0409"],["/* field 1 */\n/* field 2 */"]
]
我有代码正在解析前两个项目:
form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
text.scan(form)
Run Code Online (Sandbox Code Playgroud)
[
["F004"],["0309"],
["F004"],["0409"]
]
这里是我尝试解析所有三个并且失败并且无效的正则表达式错误的代码:
form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
form_and_fields = /#{form}(.[^#{form}]+)/m
text.scan(form_and_fields)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
你似乎误解了角色类(例如[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)
Run Code Online (Sandbox Code Playgroud)
从内到外(我将使用(?#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]]
Run Code Online (Sandbox Code Playgroud)
额外的nils来自form于在否定模式中使用的捕获组(?!#{form}),因此在成功匹配时不捕获任何内容.
这可以清理一些:
form_and_fields = /#{form}\s*(.+?)\s*(?:(?=#{form})|\Z)/m
text.scan(form_and_fields)
Run Code Online (Sandbox Code Playgroud)
现在,我们使用零宽度正向前瞻(?=#{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]]
Run Code Online (Sandbox Code Playgroud)
现在请注意,最后两个字段是第一次填充 - b/c零宽度正向前瞻中的捕获parens匹配的东西,即使它在过程中没有被标记为"消耗" - 这就是为什么这个位可以第二次重新比赛.