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

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

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

ECMAScriptとは、JavaScript類の標準を定めるために作られたスクリプト言語です。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

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

Q&A

解決済

5回答

7662閲覧

括弧の数を考慮した正規表現について

退会済みユーザー

退会済みユーザー

総合スコア0

ECMAScript

ECMAScriptとは、JavaScript類の標準を定めるために作られたスクリプト言語です。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

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

0グッド

1クリップ

投稿2018/11/04 08:22

編集2018/11/04 08:31

聞きたいこと

カッコの数が一致するように正規表現を書きたいです。
[ソード]については問題ないのですが、[]が入れ子になった場合うまく動作しません。

例えば[ソード[炎属性]という出力が得られていますが、[ソード[炎属性]]という出力が欲しいです。
最後のカッコを意識するような正規表現はどのように書けばよいでしょうか?

ソースコード

main.js

javascript

1// 無駄な文字列が入っているところから武器名を取り出すプログラム 2// aaa, bbb, ccc はダミーの文字列。 3let strs = [ 4 'aaa[ソード]bbb', 5 'aaaaa[ソード[炎属性]]bbbb', 6 'aaa[ソード][ツインソード]', 7 'aaa[ソード[炎属性]]bbb[ツインソード]ccc', 8 'aaa[ソード[炎属性]][ツインソード][ソード[水属性]]ccc' 9] 10let myRegexp = new RegExp(/[.*?]/g) 11let match = null 12 13while ((match = myRegexp.exec(strs)) !== null) { 14 let itemName = match[0] 15 console.log(itemName) 16}

実行結果

$ node main.js [ソード] [ソード[炎属性] [ソード] [ツインソード] [ソード[炎属性] [ツインソード] [ソード[炎属性] [ツインソード] [ソード[水属性]

求めている実行結果

$ node main.js [ソード] [ソード[炎属性]] [ソード] [ツインソード] [ソード[炎属性]] [ツインソード] [ソード[炎属性]] [ツインソード] [ソード[水属性]]

環境

$ node --version v8.9.1

参考

MDN - RegExp.prototype.exec()

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

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

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

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

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

guest

回答5

0

ベストアンサー

[]の入れ子が一段階までしかなければ/[(?:[^[]]|[[^]]*])*]/という正規表現で可能です。これは入れ子部分と入れ子でない部分をそれぞれわけて見ているだけとなります。

※ 入れ子が一段階のみの場合はこれで十分です。二段階以上になる場合があれば、続きをお読みください。


先程示した正規表現は入れ子が二段階には対応していません。もし、対応しようとするともっと複雑になりますし、それこそ段階がいくつになるか不明となった場合、(現行仕様の)JavaScript標準の正規表現だけでは不可能です。

括弧の対応が入れ子になっている場合でもマッチ可能にすることは一般的な正規表現ではできません。どうしても正規表現で行いたい場合は部分式呼び出しという機能を持った高度な正規表現を使う必要があります。私が知る限り、この機能を持った正規表現ライブラリはOniguruma(鬼車)(または、その改変版であるOnigmo(鬼雲))しかありません。これらを標準で採用している言語はRubyとPHP(mbstringの一部であるmb_eregの正規表現)のみです。

どうしても正規表現を使いたい

幸運なことにnpmパッケージとしてonigurumaがあります。これを使えば、Node.jsからOniguromaの正規表現を使うことが可能です。なお、node-gypでコンパイルする必要がありますので、npmでインストールする前にnode-gypの準備を整えておいてください。また、Node.jsでしか使えません。

オリジナル、一段階のみ対応、そしてOniguruma版の三つを用意しましたので、それぞれ実行結果を比べてみてください。サンプルの文字列もいくつか増やしています。

JavaScript

1// 無駄な文字列が入っているところから武器名を取り出すプログラム 2// aaa, bbb, ccc はダミーの文字列。 3import oniguruma from 'oniguruma' 4const {OnigRegExp} = oniguruma 5let strs = [ 6 'aaa[ソード]bbb', 7 'aaaaa[ソード[炎属性]]bbbb', 8 'aaa[ソード][ツインソード]', 9 'aaa[ソード[炎属性]]bbb[ツインソード]ccc', 10 'aaa[ソード[炎属性]][ツインソード][ソード[水属性]]ccc', 11 'aaa[ソード[[闇]属性]][ツインソード][ソード[[光]属性]]ccc', 12 'aaa[[エンハンス]ソード[光[[反転]邪]属性]]ccc' 13] 14 15{ 16 let myRegexp = new RegExp(/[.*?]/g) 17 let match = null 18 19 console.log(`==== ${myRegexp} ====`) 20 while ((match = myRegexp.exec(strs)) !== null) { 21 let itemName = match[0] 22 console.log(itemName) 23 } 24} 25{ 26 let myRegexp = new RegExp(/[(?:[^[]]|[[^]]*])*]/g) 27 let match = null 28 29 console.log(`==== ${myRegexp} ====`) 30 while ((match = myRegexp.exec(strs)) !== null) { 31 let itemName = match[0] 32 console.log(itemName) 33 } 34} 35 36{ 37 let pattern = '(?<paren>\[(?:\g<paren>|[^\[\]])*\])' 38 let myRegexp = new OnigRegExp(pattern) 39 let match = null 40 let startPos = 0 41 42 console.log(`==== Oniguruma: ${pattern} ====`) 43 while ((match = myRegexp.searchSync(strs, startPos)) !== null) { 44 let itemName = match[0].match 45 console.log(itemName) 46 startPos = match[0].end 47 } 48}

※ 質問者のコードにあわせて末尾セミコロン無しで書いています。
※ onigurumaパッケージの作りが悪いのか、import {OnigRegExp, OnigScanner} from 'oniguruma'だと--experimental-modulesではエラーになりました。
node --experimental-modulesとするか、babelでimportrequireに変換してから実行してください。

ブラウザの環境でも使いたい

onigurumaパッケージはNode.js上でしか使用できません。ブラウザ上で動作させるには正規表現以外の方法(他の検索やマッチを組み合わせる処理など)も使う必要があります。要望があれば実装を考えます。

投稿2018/11/04 10:17

編集2018/11/04 21:46
raccy

総合スコア21733

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

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

退会済みユーザー

退会済みユーザー

2018/11/04 13:29 編集

ご回答有り難うございます。 動作確認が取れ、どちらも期待する動作が得られました。 入れ子は1段回以上になる事は無く、1段階のみでした。 お手数をおかけして申し訳ありません。 回答頂いた2つの正規表現を2時間ほど眺めているとどちらも理解できましたが、自分で書けと言われると時間がかかりそうです…(T_T) がんばって慣れていきます。 部分式呼び出しは初めて聞きました。 以下Webサイトにいろいろ書かれており使えそうですが、 自分で書くにはスラスラかけそうにないです…(T_T) http://d.hatena.ne.jp/atzy/20080910/p1 今回はWebブラウザは無視で大丈夫です。 ありがとうございました。
think49

2018/11/04 15:16

> 先程示した正規表現は入れ子が二段階には対応していません。もし、対応しようとするともっと複雑になりますし、それこそ段階がいくつになるか不明となった場合、(現行仕様の)JavaScript標準の正規表現では不可能です。 厳密には、一度の正規表現のマッチが不可能なだけで、複数回に分けてマッチさせれば、現行仕様でも十分可能です。 私の回答欄に追記しました。
raccy

2018/11/04 21:48

>think49さん 誤解を生まないように「だけ」という表現にしました。これなら正規表現のみを使って、つまり、マッチした文字列の結合などをせずに対象を抜き出すことができないって意味で通じますでしょうか?
guest

0

そもそも(純粋な)正規表現は正規言語という形式言語を表現するための手段です。正規言語は入れ子構造を表現できません。

なので正規表現では入れ子構造のパースは無理、ということになります。

形式言語 - Wikipedia
正規言語の反復補題 - Wikipedia

投稿2018/11/04 18:12

hayataka2049

総合スコア30933

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

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

0

入れ子回数が1回

入れ子回数が1回限定なら、[] の中で [] を消費するパターンを作れば良いでしょう。

JavaScript

1console.log(/[[^[]]*(?:[[^]]*][^[]]*)*]/g.exec('aaa[ソード[炎属性]][ツインソード][ソード[水属性]]ccc')[0]); // "[ソード[炎属性]]"

入れ子回数が無制限

入れ子が無制限の場合は、

  • [
  • ]
  • [] 以外の文字列

の3つに分けてマッチさせ、入れ子深度を計測しながら、文字列にマッチさせる必要があります。

JavaScript

1matchAllCharacterPair('[a][[b]][[[c]]]', '[', ']'); // ["[a]","[[b]]","[[[c]]]"]

Re: Kyun001 さん

投稿2018/11/04 10:09

編集2018/11/04 15:12
think49

総合スコア18156

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

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

退会済みユーザー

退会済みユーザー

2018/11/04 10:38

ご回答有り難うございます。 期待する動作を得ることができました。 しかし、regex visualizer等使っても今の私には呪文にしか見えない為、 どのように正規表現で表したのかを時間をかけて理解していこうとおもいます。
guest

0

サンプルを書いてみました。質問には沿っていますが、場合により失敗する例があるので注意してください。その例についても書いています。

html

1<body> 2 <textarea id="result" style="height: 300px; width: 500px;"></textarea> 3 <script type="text/javascript" language="javascript"> 4 let strs = [ 5 'aaa[ソード]bbb', 6 'aaaaa[ソード[炎属性]]', 7 'aaa[ソード][ツインソード]', 8 'aaa[ソード[炎属性]]bbb[ツインソード]ccc', 9 'aaa[ソード[炎属性]][ツインソード][ソード[水属性]]ccc', 10 'aaa[ソード[炎属性]xx]', // 失敗する例 11 ] 12 // (?!]): 「後ろが]でない」という表現を追記しました 13 let myRegexp = new RegExp(/[.*?](?!])/g) 14 // matchを使うことで、マッチするパターン全てを得られます 15 let results = strs.map(o => o.match(myRegexp)) 16 17 document.getElementById('result').value = results 18 // 行がもつ要素を結合します 19 .map(o => o.join(', ')) 20 // 行同士を結合します 21 .reduce((a, b) => a + '\n' + b) 22 </script> 23</body>

投稿2018/11/04 09:51

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2018/11/04 10:59 編集

回答有り難うございます。 「失敗する例」を考慮するのを忘れていました。 回答いただいたところすみませんが、その場合も考える必要があった為今回は使えませんでした。 しかし、「失敗する例」が必要だった事に気づく事ができ、whileで書いていた部分をmapで書き換えられることを知る事ができました。ありがとうございます。
退会済みユーザー

退会済みユーザー

2018/11/04 11:03

ちなみに、'[ソードxx[炎属性]]'や'[xxソード[炎属性]]'となることはないんですか?
退会済みユーザー

退会済みユーザー

2018/11/04 11:08

はい。カッコの中に無駄な文字が入るようなケースはありません。
退会済みユーザー

退会済みユーザー

2018/11/04 11:11

そうすると、失敗する例として挙げた'aaa[ソード[炎属性]xx]'というケースもなさそうですか?なさそうなら、上記の方法でいけるかと。
退会済みユーザー

退会済みユーザー

2018/11/04 11:15

すみません、具体例を忘れていました。 `[ソード[炎属性]レベル2]` のような場合がありましたので、この場合一致しませんでした。
退会済みユーザー

退会済みユーザー

2018/11/04 11:18

なるほど。
guest

0

WWWクリエイターズの「正規表現:最短一致でマッチさせる表現」の記事が参考になると思います。

投稿2018/11/04 09:17

seastar3

総合スコア2285

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

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

退会済みユーザー

退会済みユーザー

2018/11/04 10:32

回答有り難うございます。 「?」を使って最短一致になっていた為、最後のカッコが一致していなかったのですね。
退会済みユーザー

退会済みユーザー

2018/11/04 10:38

?を消しちゃうと、最後の]に引っかかるようになっちゃいます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問