如何摆脱尾随空格
How to get rid of trailing whitespaces
我正在尝试在此处的 pyparsing 中完成 ISC 样式 (Bind9/DHCP) 配置解析器(在 GitHub、Google 等搜索了很长时间之后).
ISC 风格的配置文件具有以下古怪的文本属性:
- 所有C/C++/Bash评论样式
- 包含文件支持
- 分号在关键字之前终止
- 分号可能会或可能不会紧挨着标记模式
- 多行支持(分号可能在几行之后)
最接近 ISC 风格配置语法(也在 pyparsing 中)的编码风格是 NGINX,我在 there on GitHub 上看过。但这将意味着放弃 pyparsing 的自动空白处理,因为如果可能的话,我想保留它。
当我开始执行输入模糊单元测试时,我已经制作的 PyParsing 语法语法树现在处于不稳定状态。
[['server', 'example.com']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com ']]
['options', ['server', 'example.com '], ['server2', 'example2.net ']]
我有语法代码片段:
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
period = Literal(".")
semicolon = Literal(";").suppress()
domain_name = Word(srange("[0-9A-Za-z]"), min=1, max=63)
domain_name.setName("domain")
fqdn = originalTextFor(domain_name - \
originalTextFor(period - \
domain_name) * (0, 16) - \
Optional(period))
fqdn.setName("fully-qualified domain name")
orig_fqdn = originalTextFor(fqdn).setName('FQDN')
options_server = Group(Keyword("server") - fqdn - semicolon)
options_server2 = Group(Keyword("server2") - fqdn - semicolon)
options_group = Optional(options_server) & \
Optional(options_server2) \
我仍然无法摆脱尾随的空格。
已尝试以下方法无济于事:
iwsp = Optional(Word("[ \t]")).suppress() # Ignore WhiteSPace
options_server = Group(Keyword("server") - fqdn - iwsp - semicolon)
我做错了什么?
一个完整的工作 Python 片段包含在下面:
#!/usr/bin/env python3
from pyparsing import Literal, Word, srange, \
originalTextFor, Optional, ParseException, \
OneOrMore, Keyword, ZeroOrMore, \
ParseSyntaxException, Group
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
period = Literal(".")
semicolon = Literal(";").suppress()
domain_name = Word(srange("[0-9A-Za-z]"), min=1, max=63)
domain_name.setName("domain")
fqdn = originalTextFor(domain_name - \
originalTextFor(period - \
domain_name) * (0, 16) - \
Optional(period))
fqdn.setName("fully-qualified domain name")
orig_fqdn = originalTextFor(fqdn).setName('FQDN')
options_server = Group(Keyword("server") - fqdn - semicolon)
options_server2 = Group(Keyword("server2") - fqdn - semicolon)
options_group = Optional(options_server) & \
Optional(options_server2) \
# | had a bunch of other options commented out
options_clause = Keyword("options") - \
lbrack - \
options_group - \
rbrack - \
semicolon
statement = options_clause # | had a bunch of other clauses commented out
isc_style_syntax = statement
def parse_me(parse_element, test_data):
greeting = parse_element.parseString(test_data, parseAll=True)
greeting.pprint(indent=4)
if __name__ == '__main__':
parse_me(options_server, "server example.com;")
parse_me(options_server, "server example.com ;")
parse_me(options_server, "server example.com\t;")
parse_me(options_server, "server\texample.com;")
parse_me(options_server, "server\texample.com ;")
parse_me(options_server, "server\texample.com\t;")
parse_me(options_server, "server example.com ;")
parse_me(options_server, "server\t \texample.com \t ;")
parse_me(options_server, "server\t\t\texample.com\t\t\t;")
parse_me(statement, "options { server\t \texample.com \t;\n server2\t\t\t\t\t\t\t\t\t\t\t\t example2.net\t;\n}\n ;")
问题是:
fqdn = originalTextFor(domain_name - \
originalTextFor(period - \
domain_name) * (0, 16) - \
Optional(period))
由于存在重复和尾随的 Optional 位,originalTextFor 似乎一直在读取和拉入字符,直到它在重复时实际失败。但是,如果您将其更改为:
fqdn = Combine(domain_name - \
originalTextFor(period + \
domain_name) * (0, 16) - \
Optional(period))
然后你的 fqdn
将只包含非空白字符。
ParserElements 还带有自己的 runTests
方法,可以更轻松地为多个输入编写快速测试:
options_server.runTests("""
server example.com;
server example.com ;
server example.com .z;
server example.com.;
""")
将打印:
server example.com;
[['server', 'example.com']]
[0]:
['server', 'example.com']
server example.com ;
[['server', 'example.com']]
[0]:
['server', 'example.com']
server example.com .z;
^(FATAL)
FAIL: Expected ";" (at char 21), (line:1, col:22)
server example.com.;
^(FATAL)
FAIL: Expected domain (at char 19), (line:1, col:20)
(您的所有制表符测试用例并没有真正被检查,因为默认情况下 pyparsing 在开始解析之前将制表符扩展为空格。您必须调用 expr.parseWithTabs()
来禁用此功能。)
我正在尝试在此处的 pyparsing 中完成 ISC 样式 (Bind9/DHCP) 配置解析器(在 GitHub、Google 等搜索了很长时间之后).
ISC 风格的配置文件具有以下古怪的文本属性:
- 所有C/C++/Bash评论样式
- 包含文件支持
- 分号在关键字之前终止
- 分号可能会或可能不会紧挨着标记模式
- 多行支持(分号可能在几行之后)
最接近 ISC 风格配置语法(也在 pyparsing 中)的编码风格是 NGINX,我在 there on GitHub 上看过。但这将意味着放弃 pyparsing 的自动空白处理,因为如果可能的话,我想保留它。
当我开始执行输入模糊单元测试时,我已经制作的 PyParsing 语法语法树现在处于不稳定状态。
[['server', 'example.com']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com ']]
[['server', 'example.com ']]
['options', ['server', 'example.com '], ['server2', 'example2.net ']]
我有语法代码片段:
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
period = Literal(".")
semicolon = Literal(";").suppress()
domain_name = Word(srange("[0-9A-Za-z]"), min=1, max=63)
domain_name.setName("domain")
fqdn = originalTextFor(domain_name - \
originalTextFor(period - \
domain_name) * (0, 16) - \
Optional(period))
fqdn.setName("fully-qualified domain name")
orig_fqdn = originalTextFor(fqdn).setName('FQDN')
options_server = Group(Keyword("server") - fqdn - semicolon)
options_server2 = Group(Keyword("server2") - fqdn - semicolon)
options_group = Optional(options_server) & \
Optional(options_server2) \
我仍然无法摆脱尾随的空格。
已尝试以下方法无济于事:
iwsp = Optional(Word("[ \t]")).suppress() # Ignore WhiteSPace
options_server = Group(Keyword("server") - fqdn - iwsp - semicolon)
我做错了什么?
一个完整的工作 Python 片段包含在下面:
#!/usr/bin/env python3
from pyparsing import Literal, Word, srange, \
originalTextFor, Optional, ParseException, \
OneOrMore, Keyword, ZeroOrMore, \
ParseSyntaxException, Group
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
period = Literal(".")
semicolon = Literal(";").suppress()
domain_name = Word(srange("[0-9A-Za-z]"), min=1, max=63)
domain_name.setName("domain")
fqdn = originalTextFor(domain_name - \
originalTextFor(period - \
domain_name) * (0, 16) - \
Optional(period))
fqdn.setName("fully-qualified domain name")
orig_fqdn = originalTextFor(fqdn).setName('FQDN')
options_server = Group(Keyword("server") - fqdn - semicolon)
options_server2 = Group(Keyword("server2") - fqdn - semicolon)
options_group = Optional(options_server) & \
Optional(options_server2) \
# | had a bunch of other options commented out
options_clause = Keyword("options") - \
lbrack - \
options_group - \
rbrack - \
semicolon
statement = options_clause # | had a bunch of other clauses commented out
isc_style_syntax = statement
def parse_me(parse_element, test_data):
greeting = parse_element.parseString(test_data, parseAll=True)
greeting.pprint(indent=4)
if __name__ == '__main__':
parse_me(options_server, "server example.com;")
parse_me(options_server, "server example.com ;")
parse_me(options_server, "server example.com\t;")
parse_me(options_server, "server\texample.com;")
parse_me(options_server, "server\texample.com ;")
parse_me(options_server, "server\texample.com\t;")
parse_me(options_server, "server example.com ;")
parse_me(options_server, "server\t \texample.com \t ;")
parse_me(options_server, "server\t\t\texample.com\t\t\t;")
parse_me(statement, "options { server\t \texample.com \t;\n server2\t\t\t\t\t\t\t\t\t\t\t\t example2.net\t;\n}\n ;")
问题是:
fqdn = originalTextFor(domain_name - \
originalTextFor(period - \
domain_name) * (0, 16) - \
Optional(period))
由于存在重复和尾随的 Optional 位,originalTextFor 似乎一直在读取和拉入字符,直到它在重复时实际失败。但是,如果您将其更改为:
fqdn = Combine(domain_name - \
originalTextFor(period + \
domain_name) * (0, 16) - \
Optional(period))
然后你的 fqdn
将只包含非空白字符。
ParserElements 还带有自己的 runTests
方法,可以更轻松地为多个输入编写快速测试:
options_server.runTests("""
server example.com;
server example.com ;
server example.com .z;
server example.com.;
""")
将打印:
server example.com;
[['server', 'example.com']]
[0]:
['server', 'example.com']
server example.com ;
[['server', 'example.com']]
[0]:
['server', 'example.com']
server example.com .z;
^(FATAL)
FAIL: Expected ";" (at char 21), (line:1, col:22)
server example.com.;
^(FATAL)
FAIL: Expected domain (at char 19), (line:1, col:20)
(您的所有制表符测试用例并没有真正被检查,因为默认情况下 pyparsing 在开始解析之前将制表符扩展为空格。您必须调用 expr.parseWithTabs()
来禁用此功能。)