2

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>" 

しかし、<...>構造内の単語境界から単一の単語を分離することはできません。

ヘルプがいいでしょう!

PS 1

全体の話は音楽的なものです:私はリリーポンド形式の文字列(またはより良い、非常に単純なコア形式のサブセット、音符と長さだけ)を持っており、それらをPythonペアint(duration)、list(ofピッチ)に変換したいと思います文字列)。パフォーマンスは重要ではないので、それらを前後に変換したり、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]文字で構成できます。
  • 文字列の後に0桁以上の数字が続きます:e bes g4 c16
  • 文字列の後に0個以上の'または、(結合されていない)が続く:e' bes、f''' 2 g ,, 4
  • 文字列は文字列のリストで置き換えることができます。リストリミッターは<>です。4番号は>の後ろにあり、スペースは使用できません。

PS 2

目標は、Lilypondパーサーを作成することではありません。それは本当に、追加機能やメモを挿入するための拡張機能のない非常に短いスニペットのためだけのものですか?これが機能しない場合は、ABCのような別の形式(簡略化)を選択します。したがって、Lilypond(「lilypondを介して実行し、Schemeで音楽データを提供し、それを解析する」)またはそのツールチェーンに関係するものは、確かにこの質問に対する答えではありません。パッケージもインストールされていません。

4

2 に答える 2

2

あなたが一般的なパーサーを探しているのではないことは知っていますが、構文解析によりこのプロセスは非常に簡単になります。あなたのフォーマットは、私が最も初期の構文解析の例の1つとして書いた化学式パーサーに非常に似ているように見えました。

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''"])]
于 2013-02-10T23:30:09.610 に答える
1

あなたの最初の質問はこのように答えることができます:

>>> 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()このパターンの結果の前に発生する単一のスペースを削除するために追加しました。最初のオプションを使用したい場合は、単に。に置き換えることができ#<ます<

,2番目の質問は次の方法で解決できますが、のようなリストでを考える必要があるかもしれません['g,', "b'"]。文字列のコンマを含める必要がありますか?より速い方法があるかもしれません。以下は、概念実証にすぎません。リスト内包表記が最後の要素の代わりになる場合がありますが、それは非常に複雑です。

>>> s = "c'4 d8 < e' g' >16 fis'4 a,, <g, b'> c''1"
>>> q2 = re.compile(r"(?:<)\s*[^>]*\s*(?:>)\d*|(?<!<)[^\d\s<>]+\d+|(?<!<)[^\d\s<>]+")
>>> 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''"])]
于 2013-02-10T18:57:30.123 に答える