我正在使用Python(3)中的re模块,并希望以以下格式替换(re.sub(regex,replace,string))一个字符串
"foo <bar e word> f ga <foo b>"
至
"#foo <bar e word> #f #ga <foo b>"
甚至
"#foo #<bar e word> #f #ga #<foo b>"
但我无法在<…>内将单个单词与单词边界隔离开来建筑
帮忙会很好的!
p.S 1
整个故事是一个音乐故事:我有Lilypond格式的字符串(或者更好的是,非常简单的核心格式的子集,只有音符和持续时间),并希望将它们转换为python对int(持续时间)、list(音高字符串)。性能并不重要,所以我可以来回转换它们,用python列表迭代,拆分字符串并再次连接它们等等。但对于上述问题,我没有找到答案。
源字符串
"c'4 d8 < e' g' >16 fis'4 a,, <g, b'> c''1"
应导致
[
(4, ["c'"]),
(8, ["d"]),
(16, ["e'", "g'"]),
(4, ["fis'"]),
(0, ["a,,"]),
(0, ["g", "b'"]),
(1, ["c''"]),
]
基本格式是字符串+数字,如下所示:e4 bes16
- 列出项目
- 字符串可以由多个,至少一个[a-zA-Z]字符组成
- 字符串后面跟零位或多位数字:e bes g4 c16
- 字符串后面跟零个或多个"or"(未组合):e"bes,f"2 g,,4
- 字符串可以由字符串列表代替,列表限制符为<>:4数字在>后面,不允许有空格
p.S.2
目标不是创建Lilypond解析器。它真的只是非常短的片段吗?没有额外的功能,没有插入注释的扩展。如果这不起作用,我会选择另一种格式(简化),比如ABC。因此,任何与Lilypond有关的事情("通过Lilypond运行它,让它在Scheme中给出音乐数据,解析它")或其工具链肯定不是这个问题的答案。软件包甚至没有安装。
我知道您不是在寻找一个通用的解析器,但pyparsing使这个过程非常简单。您的格式似乎与我作为最早的pyparsing示例之一编写的化学公式解析器非常相似。
以下是使用pyparsing:实现的问题
from pyparsing import (Suppress,Word,alphas,nums,Combine,Optional,Regex,Group,
OneOrMore)
"""
List item
-the string can consist of multiple, at least one, [a-zA-Z] chars
-the string is followed by zero or more digits: e bes g4 c16
-the string is followed by zero or more ' or , (not combined):
e' bes, f'''2 g,,4
-the string can be substituted by a list of strings, list limiters are <>;
the number comes behind the >, no space allowed
"""
LT,GT = map(Suppress,"<>")
integer = Word(nums).setParseAction(lambda t:int(t[0]))
note = Combine(Word(alphas) + Optional(Word(',') | Word("'")))
# or equivalent using Regex class
# note = Regex(r"[a-zA-Z]+('+|,+)?")
# define the list format of one or more notes within '<>'s
note_list = Group(LT + OneOrMore(note) + GT)
# each item is a note_list or a note, optionally followed by an integer; if
# no integer is given, default to 0
item = (note_list | Group(note)) + Optional(integer, default=0)
# reformat the parsed data as a (number, note_or_note_list) tuple
item.setParseAction(lambda t: (t[1],t[0].asList()) )
source = "c'4 d8 < e' g' >16 fis'4 a,, <g, b'> c''1"
print OneOrMore(item).parseString(source)
具有此输出:
[(4, ["c'"]), (8, ['d']), (16, ["e'", "g'"]), (4, ["fis'"]), (0, ['a,,']),
(0, ['g,', "b'"]), (1, ["c''"])]
您的第一个问题可以这样回答:
>>> import re
>>> t = "foo <bar e word> f ga <foo b>"
>>> t2 = re.sub(r"(^|s+)(?![^<>]*?>)", " #", t).lstrip()
>>> t2
'#foo #<bar e word> #f #ga #<foo b>'
我添加了lstrip()
,以删除出现在该模式结果之前的单个空格。如果你想使用你的第一个选项,你可以简单地用<
替换#<
。
您的第二个问题可以通过以下方式解决,尽管您可能需要考虑像['g,', "b'"]
这样的列表中的,
。字符串中的逗号应该在那里吗?也许有一个更快的方法。以下只是概念的证明。列表理解可能会取代最后一个元素,尽管它会非常复杂。
>>> s = "c'4 d8 < e' g' >16 fis'4 a,, <g, b'> c''1"
>>> q2 = re.compile(r"(?:<)s*[^>]*s*(?:>)d*|(?<!<)[^ds<>]+d+|(?<!<)[^ds<>]+")
>>> s2 = q2.findall(s)
>>> s3 = [re.sub(r"s*[><]s*", '', x) for x in s2]
>>> s4 = [y.split() if ' ' in y else y for y in s3]
>>> s4
["c'4", 'd8', ["e'", "g'16"], "fis'4", 'a,,', ['g,', "b'"], "c''1"]
>>> q3 = re.compile(r"([^d]+)(d*)")
>>> s = []
>>> for item in s4:
if type(item) == list:
lis = []
for elem in item:
lis.append(q3.search(elem).group(1))
if q3.search(elem).group(2) != '':
num = q3.search(elem).group(2)
if q3.search(elem).group(2) != '':
s.append((num, lis))
else:
s.append((0, lis))
else:
if q3.search(item).group(2) != '':
s.append((q3.search(item).group(2), [q3.search(item).group(1)]))
else:
s.append((0, [q3.search(item).group(1)]))
>>> s
[('4', ["c'"]), ('8', ['d']), ('16', ["e'", "g'"]), ('4', ["fis'"]), (0, ['a,,']), (0, ['g,', "b'"]), ('1', ["c''"])]