在Python中将文本分割成由未引号/未注释分号分隔的多行令牌



我想使用Python将一些文本分割成token。这些令牌由未加引号、未加注释的分号分隔,我们假设"定义引号,--定义注释。代码所做的低于我想要的(至少在我考虑的测试用例上)。然而,由于我为了保留引用的--;而进行的替换,这段代码看起来相当黑客和脆弱。是否有一种更清晰、更可靠的方法来解决这个问题?

import shlex
import string
testdata = '''
Line 1
Line 2
Line 3;
Line 4;
Line 5
Line 6 -- ;
Line 7;
Line 8 ";"
Line 9 "--"
Line 10 "--;"
Line 11 ";--"
Line 12;
'''
dash_suffix = '__you_should_know_better_1__'
quote_prefix = '__you_should_know_better_2__'
s = testdata.replace('--', f'#{dash_suffix}')
s = s.replace('"', f'{quote_prefix}"')
parser = shlex.shlex(instream=s, posix=True)
parser.whitespace = ';'
parser.whitespace_split = True
for token in parser:
trimmed_token = token.strip()
if trimmed_token:
parsed_token = trimmed_token.replace(quote_prefix, '"')
parsed_token = parsed_token.replace(f'#{dash_suffix}', '--')
print(80 * '=')
print(parsed_token)

您正在使用一个专门用于解析shell脚本的词法分析器。它不是为通用解析而设计的,所以它总是一个hack。使用Python的re模块构建一个强大的词法分析器是很有可能的。检查文档中的示例标记器:

https://docs.python.org/3/library/re.html writing-a-tokenizer

我最终认为这个解析任务足够简单,所以我只编写了一个循环来遍历字符串中的字符以生成令牌。

testdata = '''
Line 1
Line 2
Line 3;
Line 4;
Line 5
Line 6 -- ;
Line 7;
Line 8 ";"
Line 9 "--"
Line 10 "--;"
Line 11 ";--"
Line 12;
'''

def advance_until(i: int, s: str, c: str, check_escape: bool = False) -> int:
while i < len(s):
if s[i] == c:
if not check_escape:
return i
if i > 0 and s[i - 1] != '\':
return i
i += 1
return i

tokens = []
token_start = 0
while token_start < len(testdata):
token_end = token_start
while token_end < len(testdata):
if testdata[token_end] == ';':
# An unescaped, unquoted semicolon terminates a statement.
tokens.append(testdata[token_start:(token_end + 1)].strip())
break
elif testdata[token_end] in ''"':
# Advance the cursor to the end of the string.
token_end = advance_until(token_end + 1, testdata,
testdata[token_end], True)
elif testdata[token_end] == '#':
# Advance the cursor to the end of the line.
token_end = advance_until(token_end + 1, testdata, 'n')
elif testdata[token_end] == '-':
# Check if this is the beginning of a comment.
if (token_end < len(testdata) - 1
and testdata[token_end + 1] == '-'):
token_end = advance_until(token_end + 2, testdata, 'n')
token_end += 1
token_start = token_end + 1
for token in tokens:
print(80 * '*')
print(token)

最新更新