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

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

ただいまの
回答率

90.12%

正規表現で入れ子になっている会話文を抜き出す

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,943

hatena19

score 4830

あるテキストから"「"~"」"に挟まれた会話文を抜き出す場合、
正規表現では下記のコードでできました。

Sub RegExpTest()
    Dim re As VBScript_RegExp_55.RegExp
    Dim mc As VBScript_RegExp_55.MatchCollection
    Dim m As VBScript_RegExp_55.Match
    Dim Data As String
    Data = "「ABCDEFG1」" & vbCrLf & _
           "「abcd" & vbCrLf & _
           "efg1」" & vbCrLf & _
           "aaa「ABCDEFG2」" & vbCrLf & _
           "bbb「abcd" & vbCrLf & _
           "efg2b」ccc"
    Set re = New RegExp
    re.Global = True
    re.MultiLine = True
    re.Pattern = "「((.|\r\n)*?)」"

    Set mc = re.Execute(Data)
    For Each m In mc
        Debug.Print "■"; m.Value; "■"
    Next
End Sub

下記のように"「"~"」"が入れ子になっている場合、
うまく抜き出せません。

    Data = "「ABC「DEF」G1」" & vbCrLf & _
           "「abc「d" & vbCrLf & _
           "ef」g1」" & vbCrLf & _
           "aaa「ABCDEFG2」" & vbCrLf & _
           "bbb「abcd" & vbCrLf & _
           "efg2b」ccc"

上記の場合、

■「ABC「DEF」■
■「abc「d
ef」■
■「ABCDEFG2」■
■「abcd
efg2b」■

というようになってしまいます。
下記のように入れ子部分も含めて抜き出すことは正規表現で可能でしょうか。

■「ABC「DEF」G1」■
■「abc「d
ef」g1」■
■「ABCDEFG2」■
■「abcd
efg2b」■

正規表現を使わずに、VBAのみでループさせて抜き出すことはできています。
正規表現だけで、あるいはVBAとうまく組み合わせて簡単にできないかなと思ったので質問させていただきました。

コードはVBAの例ですが、VBScript や JavaScript でもOKです。

追記

皆さん、回答ありがとうございました。

2階層までというように制限があれば正規表現一発でできるというのは皆様の回答で分かりました。

質問のサンプルは2階層まででしたが、できれば階層制限なしが希望でした。
質問内容が曖昧でした。

標準の正規表現では「無制限の入れ子は表現できない」、
しかし、言語によっては拡張されて可能である。
ということも分かりました。

今回の場合は、正規表現を使わずに、VBAのみで階層制限なしのロジックは完成しているのですが、
正規表現一発でできないのかな、あるいは正規表現を使ってもう少しシンプルに記述できないかなと、疑問の思ったのが質問に至った経緯です。

皆さんの回答は、それぞれみな参考になりましたので、どれをベストアンサーにするのか迷いましたが、
有用なロジックと、実際に使える汎用性の高いコードを提示していただいた、think49 さんとさせていただきました。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

 括弧が2階層までの場合

既に指摘されていますが、2階層までなら正規表現で一度にマッチさせる事が可能です。

var string = ['「a」', '「「b」」','「「c」「d」」'].join();
console.log(string.match(/「(?:[^「」]*「[^「」]*」)*[^「」]*」/g));  // ["「a」", "「「b」」", "「「c」「d」」"]

 3階層以上の括弧にマッチさせるには

3階層以上の括弧に対応する為には開始文字()の数と終了文字()の回数が一致するまで再帰的にマッチさせる必要があります。
RegExp#exec で再帰的にマッチさせれば可能です。
が、注意点として 「「「a」 のように対応する括弧で閉じられていないパターンの為にバックトラック処理(マッチに失敗した場合に失敗前の場所まで戻ってマッチをやり直す)が必要になります。

  1. 「「「a」 でマッチ失敗
  2. 「「a」 でマッチ失敗
  3. 「a」 でマッチ成功

ちなみに、2文字以上の文字列を受け付ける汎用性を持たせれば、対応するHTMLタグにマッチさせる事も可能です。

 サンプルコード

GitHubにサンプルコードをUPしました。
質問の要件は満たせていると思います。

var string;

/**
 * 「xxx」
 */
string = '「「「a」, 「「b」」, 「「c」「d」」, 「「「e」「f」」」, 「「「g」」「「h」」」';
console.log(JSON.stringify(findCorrespondingFromString(string, '「', '」'))); // ["「a」","「「b」」","「「c」「d」」","「「「e」「f」」」","「「「g」」「「h」」」"]

/**
 * <<xxx>>
 */
string = '<<a>>,<<<<b>>>>,<<<<c>><<d>>>>';
console.log(JSON.stringify(findCorrespondingFromString(string, '<<', '>>'))); // ["<<a>>","<<<<b>>>>","<<<<c>><<d>>>>"]

/**
 * <div>xxx</div>
 */
string = `<div id="parent">
  <p>text</p>
  <div class="child">
    <p>text</p>
    <p>text</p>
    <div class="grandson">
      <p>text</p>
    </div>
    <div class="grandson">grandson2</div>
  </div>
  <div class="child">child2</div>
</div>`;
console.log(JSON.stringify(findCorrespondingFromString(string, '<div(?:\\s[^>]*)?>', '</div>'))); // [<div id="parent">...</div>]

 更新履歴

  • 2017/05/13 02:13 [サンプルコード] の節を追記。
  • 2017/05/13 08:04 サンプルコードを追記。

Re: hatena19 さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

これは情報科学の話になってくるのですが、「正規表現」という言葉自体が実は「入れ子は表現できない」という意味を含んでいるのです…

ただ、PerlやRuby、Pythonだと正規表現を鬼拡張した結果入れ子を表現できるようになってしまっており、つまりこれは実は正規表現と呼ぶべきではない代物であったりするのですが、ここら辺のスクリプト言語を使うことをご検討いただければ。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

単に二重の入れ子まででよいなら条件を列記すればいいでしょう

「[^「]*?」|「.*?「[^「]*?」.*」

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる