
Go言語で、文字列内で{{..}}で括られた部分を正規表現を使い、以下のように取得したいのですが、{{..}}内に更に{{..}}がある、入れ子になってるとうまく取得できません。
{{今日}}はいい{{天気}}。 → 「今日」「天気」と取得
{{今日は{{いい}}天気}}。 → 「今日は{{いい}}天気」と取得
何か解決案がありましたら宜しくお願いします。
※入れ子が何階層になっても問題なく取得できるものとします
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答2件
0
ベストアンサー
正規表現で入れ子になっている括弧の対応を無制限に認識するようにするには、再帰的なマッチが行える仕組みを持っている必要があります。PerlのRecursive subpattern(うまくアンカーにリンクが張れないので(?R)
で検索してください)やRubyのsubexpression callをサポートする正規表現ではなくてはなりません。この機能は正規表現の拡張であり、全ての言語の標準組み込みされた正規表現に備わっているわけではありません。PerlとRuby以外ではPCREや鬼車またはその改良版の鬼雲(Rubyの組み込み正規表現エンジン)をそのまま組み込んでいるPHP(PCREのpreg系と鬼車のmb_ereg系)、Delphi、R等だけです。
参考:
PerlとRubyの例
Perl
1'{{今日は{{いい天気}}。' =~ /\{\{((?:(?0)|\}?[^\}])*)\}\}/; 2print $1;
Ruby
1'{{今日は{{いい}}天気}}。' =~ /\{\{((?:\g<0>|\}?[^\}])*)\}\}/ 2puts $1
Go標準の正規表現ではSyntaxには(?R)
がNOT SUPPORTEDと明記されていますので、対応はしていません。Goでどうしても使いたい場合は、pcreのパッケージ、onigurumaのパッケージ、onigmoのパッケージ等を使うか、Cのライブラリを直接読み込んで、上の例のような正規表現にする必要があります。
投稿2020/01/28 12:00
総合スコア21741
0
こんにちは
まず、考え方を示し、その後、Golangによるコード例を挙げます。
考え方
プログラミング言語は異なりますが、昨日(1/27)に、以下の類似の質問がありました。
上記に対する、私の回答 や、この回答から参照されている、さらに以前の類似質問への回答 にも書きましたが、開くカッコと閉じるカッコがきちんと対応して出現し、かつ、カッコのネストもあるような文字列にマッチする正規表現を書くことは(標準の正規表現では)困難です。
このご質問の場合も、上記の質問と同様に、所与の文字列を、以下のようなXMLにしてパースした後に、XPath で
/root/node
に相当する要素を取り出せばよいかと思います。
(1) "{{今日}}はいい{{天気}}。"
という文字列を、以下のXMLに変換
xml
1<root> 2 <node>今日</node>はいい<node>天気</node>。 3</root>
(2) {{今日は{{いい}}天気}}。"
という文字列を、以下のXMLに変換
xml
1<root> 2 <node>今日は<node>いい</node>天気</node>。 3</root>
上記のようなXMLにすること自体は、 {{
を <node>
に、 }}
を </node>
に置き換えて、<root>・・・</root>
で囲めばよいので、単純な文字列操作で出来ます。
XMLが出来れば、Goで用意されているXML処理のパッケージを使ってパースし、欲しい情報を取得すればよいかと思います。
コード例
以下、上記の考え方で作成したコード例です。XMLのパースとXPathによる要素取得のために、github.com/antchfx/xmlquery を使いました。
Golang
1package main 2 3import ( 4 "fmt" 5 "github.com/antchfx/xmlquery" 6 "log" 7 "strings" 8) 9 10func getXML(text string) string { 11 text = strings.Replace(text, "{{", "<node>", -1) 12 text = strings.Replace(text, "}}", "</node>", -1) 13 text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 14 "<root>\n" + 15 text + 16 "\n</root>" 17 18 return text 19} 20 21func getFirstLevelBracketedTokens(text string) []string { 22 xml := getXML(text) 23 24 doc, err := xmlquery.Parse(strings.NewReader(xml)) 25 if err != nil { 26 log.Fatal(err) 27 return nil 28 } 29 30 var tokens []string 31 for _, n := range xmlquery.Find(doc, "/root/node") { 32 str := strings.Replace(n.OutputXML(false), "<node>", "{{", -1) 33 str = strings.Replace(str, "</node>", "}}", -1) 34 tokens = append(tokens, str) 35 } 36 37 return tokens 38} 39 40func main() { 41 tokens := getFirstLevelBracketedTokens("{{今日}}はいい{{天気}}。") 42 fmt.Println(tokens) 43 44 tokens = getFirstLevelBracketedTokens("{{今日は{{いい}}天気}}。") 45 fmt.Println(tokens) 46} 47
上記のコードで、 関数 getFirstLevelBracketedTokens(text string)
は、
"{{今日}}はいい{{天気}}。"
に対しては、["今日","天気"]
を返します。"{{今日は{{いい}}天気}}。"
に対しては、["今日は{{いい}}天気"]
を返します。
以下にて動作確認できます。
- 動作確認用Repl.it https://repl.it/@jun68ykt/Q238025
もし、上記のようなXML(などの他のフォーマット)に変換してそれに対する既存のパーサーを使うという方法を採らずに、直接、所与の文字列をパースするコードを自作しようとすると、左から読んでいき、 {{
が出現するたびに、それを何らかのスタック構造に積み、 }}
が見つかったらスタックからポップさせるような(プッシュダウンオートマトンの動きをする)プログラムを書くことになり、それなりに書くのに労力を要するものになるのではと思います。
以上、参考になれば幸いです。
投稿2020/01/28 08:52
編集2020/01/29 00:02総合スコア9058
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/01/28 14:20
退会済みユーザー
2020/01/29 01:04