質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

2回答

1761閲覧

Go言語で正規表現を使い{{}}で括られた文字列を抜き出したい

退会済みユーザー

退会済みユーザー

総合スコア0

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

1グッド

1クリップ

投稿2020/01/28 06:57

Go言語で、文字列内で{{..}}で括られた部分を正規表現を使い、以下のように取得したいのですが、{{..}}内に更に{{..}}がある、入れ子になってるとうまく取得できません。

{{今日}}はいい{{天気}}。 → 「今日」「天気」と取得

{{今日は{{いい}}天気}}。 → 「今日は{{いい}}天気」と取得

何か解決案がありましたら宜しくお願いします。

※入れ子が何階層になっても問題なく取得できるものとします

s8_chu👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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

raccy

総合スコア21741

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jun68ykt

2020/01/28 14:20

ここまで出来る正規表現の拡張があるとは知りませんでした。大変勉強になりました。
退会済みユーザー

退会済みユーザー

2020/01/29 01:04

なるほど勉強になります。Goでもパッケージ使えば実装できるのですね。迷ったのですが、簡単に対応できそうなこちらをベストアンサーにさせて頂きました。回答どうも有り難うございます!
guest

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) は、

  • "{{今日}}はいい{{天気}}。" に対しては、 ["今日","天気"] を返します。
  • "{{今日は{{いい}}天気}}。" に対しては、 ["今日は{{いい}}天気"] を返します。

以下にて動作確認できます。

もし、上記のようなXML(などの他のフォーマット)に変換してそれに対する既存のパーサーを使うという方法を採らずに、直接、所与の文字列をパースするコードを自作しようとすると、左から読んでいき、 {{ が出現するたびに、それを何らかのスタック構造に積み、 }} が見つかったらスタックからポップさせるような(プッシュダウンオートマトンの動きをする)プログラムを書くことになり、それなりに書くのに労力を要するものになるのではと思います。

以上、参考になれば幸いです。

投稿2020/01/28 08:52

編集2020/01/29 00:02
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/01/29 01:11

この考え方は他のケースでも応用が利きそうですね。XMLで処理する発想はありませんでした。ご丁寧にコードまで書いて頂いて分かりやすかったです。回答どうも有り難うございました!
jun68ykt

2020/01/29 01:15

どういたしまして。参考になれば幸いです。私も勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問