25

タイトルが示すように、入力例は次のとおりです。

 (outer
   (center
     (inner)
     (inner)
   center)
 ouer)
 (outer
   (inner)
 ouer)
 (outer
 ouer)

もちろん、一致した文字列は再帰によって処理されます。

最初の再帰を一致させたい:

 [
 (outer
   (center
     (inner)
     (inner)
   center)
 ouer),
 (outer
   (inner)
 ouer),
 (outer
 ouer)]

そして後工程は言うまでもなく…

4

4 に答える 4

36

多くの正規表現の実装では、任意の量のネストに一致させることはできません。ただし、Perl、PHP、および .NET は再帰パターンをサポートしています。

Perl でのデモ:

#!/usr/bin/perl -w

my $text = '(outer
   (center
     (inner)
     (inner)
   center)
 ouer)
 (outer
   (inner)
 ouer)
 (outer
 ouer)';

while($text =~ /(\(([^()]|(?R))*\))/g) {
  print("----------\n$1\n");
}

印刷されます:

----------
(外側
   (中心
     (内側)
     (内側)
   中心)
 上)
----------
(外側
   (内側)
 上)
----------
(外側
 上)

または、同等の PHP:

$text = '(outer
   (center
     (inner)
     (inner)
   center)
 ouer)
 (outer
   (inner)
 ouer)
 (outer
 ouer)';

preg_match_all('/(\(([^()]|(?R))*\))/', $text, $matches);

print_r($matches);

これは以下を生成します:

配列
(
    [0] => 配列
        (
            [0] => (外側
   (中心
     (内側)
     (内側)
   中心)
 上)
            [1] => (外側
   (内側)
 上)
            [2] => (外側
 上)
        )

...

説明:

( # グループ 1 を開始
  \( # リテラル '(' に一致
  ( # グループ 2
    [^()] # '(' と ')' 以外の任意の文字
    | | # また
    (?R) # パターン全体に再帰的にマッチ
  )* # グループ 2 を終了し、0 回以上繰り返す
  \) # リテラル ')' に一致
) # グループ 1 を終了

編集

@Goozakのコメントに注意してください:

\(((?>[^()]+)|(?R))*\)より良いパターンは( PHP:Recursive patternsから)かもしれません。私のデータでは、Bart のパターンは、ネストされていない (長い文字列) に遭遇したときに PHP をクラッシュさせていました。このパターンは、すべてのデータを問題なく通過しました。

于 2013-02-19T08:12:33.450 に答える
25

正規表現を使用しないでください。

代わりに、単純な再帰関数で十分です。一般的な構造は次のとおりです。

def recursive_bracket_parser(s, i):
    while i < len(s):
        if s[i] == '(':
            i = recursive_bracket_parser(s, i+1)
        elif s[i] == ')':
            return i+1
        else:
            # process whatever is at s[i]
            i += 1
    return i

たとえば、入力をネストされたリスト構造に解析する関数は次のとおりです。

def parse_to_list(s, i=0):
    result = []
    while i < len(s):
        if s[i] == '(':
            i, r = parse_to_list(s, i+1)
            result.append(r)
        elif s[i] == ')':
            return i+1, result
        else:
            result.append(s[i])
            i += 1
    return i, result

これを次のように呼び出すとparse_to_list('((a) ((b)) ((c)(d)))efg')、結果が生成され[[['a'], ' ', [['b']], ' ', [['c'], ['d']]], 'e', 'f', 'g']ます。

于 2013-02-19T07:58:53.517 に答える
0

nneonneoからの上記の投稿に基づく Delphi Pascal コード:

btnRun という名前の、ボタンのあるフォームが必要です。ソース コードで、「arnolduss」を DownLoads フォルダー内の自分の名前に置き換えます。ParseList によって作成された出力のスタック レベルに注意してください。明らかに、同じタイプのブラケットは同じスタック レベルで開閉する必要があります。これで、スタック レベルごとにいわゆるグループを抽出できるようになります。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

Type TCharPos = record
  Level: integer;
  Pos: integer;
  Char: string;
end;

type
  TForm1 = class(TForm)
    btnRun: TButton;
    procedure btnRunClick(Sender: TObject);
  private
    { Private declarations }
    procedure FormulaFunctionParser(var CharPos: array of TCharPos;
       const formula, LBracket, RBracket: string; var itr, iChar,
       iLevel: integer; var ParseList: TStringList);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnRunClick(Sender: TObject);
var
  formula: string;
  CharPos: array of TCharPos;
  itr, iChar, iLevel: integer;
  ParseList: TStringList;
begin
  screen.Cursor := crHourGlass;
  itr := 0;
  iChar := 1;
  iLevel := 0;
  ParseList := TStringList.Create;
  try
    formula := Trim('add(mul(a,add(b,c)),d) + e - sub(f,g)');
    ParseList.Add(formula);
    ParseList.Add('1234567890123456789012345678901234567890');

    SetLength(CharPos, Length(formula));
    FormulaFunctionParser(CharPos[0], formula, '(', ')', itr, iChar, iLevel, ParseList);
  finally
    ParseList.SaveToFile('C:\Users\arnolduss\Downloads\ParseList.txt');
    FreeAndNil(ParseList);
    screen.Cursor := crDefault;
  end;
end;

//Based on code from nneonneo in: https://stackoverflow.com/questions/14952113/how-can-i-match-nested-brackets-using-regex
procedure TForm1.FormulaFunctionParser(var CharPos: array of TCharPos;
       const formula, LBracket, RBracket: string; var itr, iChar,
       iLevel: integer; var ParseList: TStringList);
  procedure UpdateCharPos;
  begin
    CharPos[itr-1].Level := iLevel;
    CharPos[itr-1].Pos := itr;
    CharPos[itr-1].Char := formula[iChar];

    ParseList.Add(IntToStr(iLevel) + '  ' + intToStr(iChar) + ' = ' + formula[iChar]);
  end;
begin
  while iChar <= length(formula) do
  begin
    inc(itr);
    if formula[iChar] = LBracket then
    begin
      inc(iLevel);
      UpdateCharPos;
      inc(iChar);
      FormulaFunctionParser(CharPos, formula, LBracket, RBracket, itr, iChar, iLevel, ParseList);
    end
    else
    if formula[iChar] = RBracket then
    begin
      UpdateCharPos;
      dec(iLevel);
      inc(iChar);
    end
    else
    begin
      UpdateCharPos;
      inc(iChar);
    end;
  end;
end;

end.
于 2017-04-04T11:48:26.017 に答える