我有一个类似C的源代码,我试图将此源代码中的所有字符串提取并保存到列表中,而不包括注释中的字符串。 此源代码中的字符串可以包含任何字符,空格,甚至注释。
示例:
// this is an inline comment with the name "Alpha 1"
string x = "Alpha 2";
/** this is a block comment with the string "Alpha 3" */
foo("Alpha 4");
string y = "Alpha /* */ 5 // with comments";
输出:
["Alpha 2", "Alpha 4", "Alpha /* */ 5 // with comments"]
我无法使用正则表达式的问题因为我可以在给定字符串中有注释(这是有效的),当然我可以在内联注释或块注释中包含字符串。
我使用此方法获取代码中的所有字符串:
re.findall(r'\"(.+?)\"', code)
但它也给了我评论中的字符串。
任何帮助?
答案 0 :(得分:2)
如果语言和你描述的一样简单,我想我会手工编写解析器。我仍然使用正则表达式来标记输入。
你走了:
import re
from itertools import takewhile
def extract_strings(source):
def consume(it, end):
return list(takewhile(lambda x: x != end, it))
tokens = iter(re.split(r'''("|/\*|\*/|//|\n)''', source))
strings = []
for token in tokens:
if token == '"':
strings.append(''.join(consume(tokens, '"')))
elif token == '//':
consume(tokens, '\n')
elif token == '/*':
consume(tokens, '*/')
return strings
data = '''
// this is an inline comment with the name "Alpha 1"
string x = "Alpha 2";
/** this is a block comment with the string "Alpha 3" */
foo("Alpha 4");
string y = "Alpha /* */ 5 // with comments";
'''
print(extract_strings(data))
答案 1 :(得分:1)
re = r'(?:\/\/.+\n|\/\*.+\*\/)|(\".+\"|\'.+\')'
这应该主要起作用。只需将// comment
之类的注释以换行符结尾即可。所有不在评论中的单词都将在捕获组1中返回。但请注意,代码中的每条评论都会None
。
答案 2 :(得分:1)
假设:
>>> src='''\
... // this is an inline comment with the name "Alpha 1"
...
... string x = "Alpha 2";
... /** this is a block comment with the string "Alpha 3" */
... foo("Alpha 4");
... string y = "Alpha /* */ 5 // with comments";'''
这个正则表达式将起作用:
>>> pat=re.compile(r"(?:\/\/.+$|/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)|(\"[^\"]*\"|\'[^']*\')", re.M)
>>> [m.group(1) for m in pat.finditer(src) if m.group(1)]
['"Alpha 2"', '"Alpha 4"', '"Alpha /* */ 5 // with comments"']
解释正则表达式here。
(但使用解析器更健壮......)
答案 3 :(得分:0)
关于编写解析器的问题,我借此机会编写自己的状态机:
import sys
ASTERISK = '*'
DEFAULT = 'default'
EOL = '\n'
ESCAPE = '\\'
QUOTE = '"'
SLASH = '/'
class ExtractStrings:
def __init__(self, multiline_string):
self.buffer = multiline_string
self.chars_collected = ''
self.strings = None
def noop(self, ch):
pass
def collect_char(self, ch):
self.chars_collected += ch
def return_string(self, ch):
self.strings.append(self.chars_collected)
self.chars_collected = ''
def parse(self):
self.strings = []
state = {
'start': {
QUOTE: (self.noop, 'in_string'),
SLASH: (self.noop, 'first_slash'),
DEFAULT: (self.noop, 'start'),
},
'in_string': {
QUOTE: (self.return_string, 'start'),
ESCAPE: (self.collect_char, 'escaping'),
DEFAULT: (self.collect_char, 'in_string'),
},
'escaping': {
DEFAULT: (self.collect_char, 'in_string'),
},
'first_slash': {
SLASH: (self.noop, 'line_comment'),
ASTERISK: (self.noop, 'block_comment'),
DEFAULT: (self.noop, 'start'),
},
'line_comment': {
EOL: (self.noop, 'start'),
DEFAULT: (self.noop, 'line_comment'),
},
'block_comment': {
ASTERISK: (self.noop, 'near_comment_block_end'),
DEFAULT: (self.noop, 'block_comment'),
},
'near_comment_block_end': {
SLASH: (self.noop, 'start'),
ASTERISK: (self.noop, 'near_comment_block_end'),
DEFAULT: (self.noop, 'block_comment'),
}
}
current = 'start'
for ch in self.buffer:
default = state[current][DEFAULT]
action, next_state = state[current].get(ch, default)
action(ch)
current = next_state
def __iter__(self):
if self.strings is None:
self.parse()
return iter(self.strings)
if __name__ == '__main__':
with open(sys.argv[1]) as f:
code = f.read()
for string_literal in ExtractStrings(code):
print('"%s"' % string_literal)
状态机定义了不同的状态,在每个状态下做什么(图中未显示)以及到下一个状态的转换。一旦 定义状态机(作为嵌套字典),只需执行状态操作,读取下一个字符并查看状态机以查看我们应该转换到哪个状态。
状态机是嵌套字典。对于外部字典,键是状态名称,值是内部字典。对于内部字典,键是下一个字符,值是元组(操作,下一个状态)。