为什么'扫描'读取多行

时间:2017-08-07 12:01:11

标签: ruby regex

我的测试配置文件(test_config.conf)如下所示

[DEFAULT]
system_name=
#test

flag=true

我想阅读此内容并使用预期输出"system_name"扫描密钥nil的值。我可以使用配置解析器来读取内容,但使用scan是我的要求。

我做了:

File.read
  • 扫描:file_data.scan(/^#{each}\s*=\s*(?!.*#)\s*(.*)/)
  • 正则表达式:^system_name\s*=\s*(?!.*#)\s*(.*)$

我使用(?!.*#)来忽略以#开头的值。

返回#test。有人可以帮助我理解为什么会这样做,以及如何改变我的正则表达式以使其按预期工作?

1 个答案:

答案 0 :(得分:1)

另一种情况是回溯如何混淆正则表达式用户。 (?!.*#)否定前瞻必须与#后未紧跟的位置匹配。由于前面的模式部分可以以各种方式匹配字符串,一旦失败,正则表达式引擎重试量化的子模式。因此,在您的情况下,\s*匹配0个或更多的空格。一旦正则表达式引擎匹配=之后的所有空格,它就会找到# - 并失败。然后回溯:尝试匹配零空格。并且发现#之后没有=。并且成功。

使用\s*+的占有量词来禁止回溯:

^system_name\s*=\s*+(?!#)(.*)$
                   ^

请参阅Rubular demo。因此,在所有0+空格匹配后,前瞻只会运行一次。如果匹配不匹配,整场比赛将立即失败。

另一种方法是使用[^\s#]否定字符类:

^system_name\s*=\s*([^\s#].*)$
                   ^^^^^^^

查看另一个Rubular demo

此处,[^\s#]仅匹配不是空格的字符,也不匹配#,然后.*将匹配除换行符之外的任何0 +字符。

根据评论中的反馈,输入的结构可能相当松散,而key = value可以跟在system_name行之后。在这种情况下,您还需要确保您捕获的文本实际上并不是以=符号后面的一些单词字符开头:

/^system_name\s*=\s*+(?!#|\w+=)(.*)$/

请参阅this Rubular demo

完整模式详情

  • ^ - 开始行
  • system_name - 文字子字符串
  • \s* - 0个或更多空格
  • = - 等号
  • \s*+ - 0个或更多空格,由于*+占有量词,没有回溯到模式中
  • (?!#|\w+=) - 如果在当前位置的右侧(即紧随其后)找到#或1+个字符,然后=,则表示匹配失败的否定前瞻0+空格)
  • (.*) - 第1组:直到行尾的任何0+字符
  • $ - 行尾。