Raku 语法:使用命名正则表达式而不消耗匹配字符串
Raku Grammar: Use named regex without consuming matching string
我有一个可能很容易回答的 Raku 语法问题。我不会解析日志文件并通过日志条目取回条目日志条目。日志条目可以只是一行或多行字符串。
我的代码草稿如下所示:
grammar Grammar::Entries {
rule TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO ' | 'ERROR' }
token logentry { .*? <.finish> }
token finish { <.loglevel> || $ }
}
这仅适用于第一行,因为在第二行中日志级别被第一行匹配消耗,尽管我使用了“.”。在正则表达式 <> 中据我所知意味着非捕获。
日志示例如下:
INFO 2020-01-22T11:07:38Z PID[8528] TID[6736]: Current process-name: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
INFO 2020-01-22T11:07:38Z PID[8528] TID[6736]: Session data:
PID: 1234
TID: 1234
Session: 1
INFO 2020-01-22T11:07:38Z PID[8528] TID[6736]: Clean up.
即使对于多行日志条目,取回日志条目的正确方法是什么?
谢谢!
as far as I know <.loglevel>
means non-capturing.
这意味着非捕获(不要保留匹配项以便代码稍后可以访问它),而不是非匹配 .
你要做的是匹配而不推进匹配位置,即所谓的"zero-width assertion"。我还没有测试过这个,但希望它能工作(著名的遗言):
grammar Grammar::Entries {
rule TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO ' | 'ERROR' }
token logentry { .*? <.finish> }
token finish { <?loglevel> || $ } # <-- the change
}
.*?
有效但效率低下。
它必须做很多回溯。
要改进它,您可以使用 \N*
匹配除换行符以外的所有内容。
grammar Grammar::Entries {
rule TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token logentry { \N* \n }
}
然后你必须添加新行匹配。
token logentry {
<logline>* %% \n
}
token logline { <!before \w> \N* }
这会起作用,但仍然不是很好。
我会更像您要解析的内容来构造语法。
grammar Grammar::Entries {
token TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token logentry { <logline>* }
token logline { ' ' <(\N+)> \n? }
}
因为我注意到日志行总是以 4 个空格开头,我们可以使用它来确保只有以那个开头的行才算作 logline
。这也处理了日志级别的行上剩余的数据。
我真的不喜欢你有一个复数名称的标记只匹配一个东西。
基本上我会将 logentries
命名为 logentry
。当然,这意味着 logentry
也需要更改名称。
grammar Grammar::Entries {
token TOP { <logentry>+ }
token logentry { <loglevel> <logdata> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token logdata { <logline>* }
token logline { ' ' <(\N+)> \n? }
}
我也不喜欢附加到每个标记的冗余 log
。
grammar Grammar::Entries {
token TOP { <entry>+ }
token entry { <level> <data> }
token level { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token data { <line>* }
token line { ' ' <(\N+)> \n? }
}
所以这说明 Grammar::Entries
至少包含一个 entry
.
entry
以 level
开头,以 data
.
结尾
data
由任意数量的 line
s
组成
line
以四个空格开头,至少一个非换行符,可以以换行符结尾。
我想表达的意思是按照数据的结构来构建语法。
您甚至可以添加用于提取信息的结构,这样您就不必在第二步中执行此操作。
我有一个可能很容易回答的 Raku 语法问题。我不会解析日志文件并通过日志条目取回条目日志条目。日志条目可以只是一行或多行字符串。
我的代码草稿如下所示:
grammar Grammar::Entries {
rule TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO ' | 'ERROR' }
token logentry { .*? <.finish> }
token finish { <.loglevel> || $ }
}
这仅适用于第一行,因为在第二行中日志级别被第一行匹配消耗,尽管我使用了“.”。在正则表达式 <> 中据我所知意味着非捕获。
日志示例如下:
INFO 2020-01-22T11:07:38Z PID[8528] TID[6736]: Current process-name: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
INFO 2020-01-22T11:07:38Z PID[8528] TID[6736]: Session data:
PID: 1234
TID: 1234
Session: 1
INFO 2020-01-22T11:07:38Z PID[8528] TID[6736]: Clean up.
即使对于多行日志条目,取回日志条目的正确方法是什么? 谢谢!
as far as I know
<.loglevel>
means non-capturing.
这意味着非捕获(不要保留匹配项以便代码稍后可以访问它),而不是非匹配 .
你要做的是匹配而不推进匹配位置,即所谓的"zero-width assertion"。我还没有测试过这个,但希望它能工作(著名的遗言):
grammar Grammar::Entries {
rule TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO ' | 'ERROR' }
token logentry { .*? <.finish> }
token finish { <?loglevel> || $ } # <-- the change
}
.*?
有效但效率低下。
它必须做很多回溯。
要改进它,您可以使用 \N*
匹配除换行符以外的所有内容。
grammar Grammar::Entries {
rule TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token logentry { \N* \n }
}
然后你必须添加新行匹配。
token logentry {
<logline>* %% \n
}
token logline { <!before \w> \N* }
这会起作用,但仍然不是很好。
我会更像您要解析的内容来构造语法。
grammar Grammar::Entries {
token TOP { <logentries>+ }
token logentries { <loglevel> <logentry> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token logentry { <logline>* }
token logline { ' ' <(\N+)> \n? }
}
因为我注意到日志行总是以 4 个空格开头,我们可以使用它来确保只有以那个开头的行才算作 logline
。这也处理了日志级别的行上剩余的数据。
我真的不喜欢你有一个复数名称的标记只匹配一个东西。
基本上我会将 logentries
命名为 logentry
。当然,这意味着 logentry
也需要更改名称。
grammar Grammar::Entries {
token TOP { <logentry>+ }
token logentry { <loglevel> <logdata> }
token loglevel { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token logdata { <logline>* }
token logline { ' ' <(\N+)> \n? }
}
我也不喜欢附加到每个标记的冗余 log
。
grammar Grammar::Entries {
token TOP { <entry>+ }
token entry { <level> <data> }
token level { 'DEBUG' | 'WARN' | 'INFO' | 'ERROR' }
token data { <line>* }
token line { ' ' <(\N+)> \n? }
}
所以这说明 Grammar::Entries
至少包含一个 entry
.
entry
以 level
开头,以 data
.
结尾
data
由任意数量的 line
s
组成
line
以四个空格开头,至少一个非换行符,可以以换行符结尾。
我想表达的意思是按照数据的结构来构建语法。
您甚至可以添加用于提取信息的结构,这样您就不必在第二步中执行此操作。