key = value
のペアは、次の規則を使用して任意のテキストに一致する必要があります。インデントされた継続行を一致させるための正規表現
- 大手行は以下の構造を有する:インデント付き
- 開始 - 「二つのスペースまたはタブ」のLEA一度、例えば:
( |\t)+
+
文字と1つのスペース- 言葉
VAR
かCONST
- および
key
およびvalue
=
文字 の
- 開始 - 「二つのスペースまたはタブ」のLEA一度、例えば:
例:
+ VAR somename = somevalue (indented with two spaces)
+ VAR name3 = indented by one \t
正規表現一致以下、このような行:
/^( |\t)+\+\s+(VAR|CONST)\s+(\w+)\s*=\s*(.*)$/
今問題:構文は、継続行可能例えば上記の行の後に少なくとも1つのインデントシーケンス( |\t)
(別名2つのスペースまたは1つのタブ)を開始する行が続く行とみなされ、その全体の内容(先行スペースも含む)は前のキーのvalue
である必要がありますライン。
例:
+ VAR multi = 3 line value where the continuation lines
are indented (starts with two spaces or one tab)
and NOT followed by the '+'
例えば、継続行ための正規表現は、溶液は、例えば、ラインベースアプローチに容易である
/^( |\t)+([^\+](.*))$/
あります私はテキスト全体を複数の行に分割し、行単位で処理します。
しかし、(複雑な)正規表現(主に学習とベンチマークの目的)を探しています。これは、キー=値のペアを1行または複数行の形式で一致させることができます。この試みた:
while($text =~ m/^( |\t)+\+\s+(VAR|CONST)\s+(\w+)\s*=\s*((.*)$(?=( |\t)+[^\+](.*)$)*)/gm) {
...
}
を私が得た:
(?=( |\t)+[^\+](.*)$)* matches null string many times in regex; marked by <-- HERE in m/^( |\t)+\+\s+(VAR|CONST)\s+(\w+)\s*=\s*((.*)$(?=( |\t)+[^\+](.*)$)* <-- HERE)/ at so line 36.
サイド質問:正規表現が含まれている必要がありますとき
/
^( |\t)+ # <- space ... :(
\+\s+
(VAR|CONST)
\s+
(\w+)
\s*=\s*
(.*)$
/x
:のように、複数行に拡張正規表現を使用する方法を正確にスペース文字(例えばユニバーサル\s
は使用できません)?
ヘルプが必要な場合は、必要な出力(行ベースのアプローチを使用)と非機能のregex-based
ソリューションを生成するコードを示します。
EDIT
#!/usr/bin/env perl
use 5.014;
use warnings;
use Data::Dumper;
my $txt = do { local $/; <DATA> };
my @matches1 = parse_by_lines($txt // '');
mydump('BY LINES', @matches1);
my @matches2 = parse_by_one_regex($txt // '');
mydump('REGEX', @matches2);
sub parse_by_lines { #produces the wanted output
my ($text) = @_;
my @match;
my $havekey;
for my $line (split "\n", $text) {
if($line =~ m/^( |\t)+\+\s+(VAR|CONST)\s+(\w+)\s*=\s*(.*)$/) {
push @match, { indent => $1, type => $2, key => $3, val => $4 };
$havekey++;
}
elsif($havekey && $line =~ m/^( |\t)+([^\+](.*))$/) { #continuation line
$match[-1]->{val} .= "\n$line"; #prserve the \n in the val
}
else {
$havekey = 0;
}
}
return @match;
}
sub parse_by_one_regex { #not working
my ($text) = @_;
my @match;
while($text =~ m/^( |\t)+\+\s+(VAR|CONST)\s+(\w+)\s*=\s*((.*)$(?=( |\t)+[^\+](.*)$)*)/gm) {
push @match, { indent => $1, type => $2, key => $3, val => $4 };
}
return @match;
}
sub mydump {
my($label, @match) = @_;
say "#### $label ####";
for my $m (@match) {
printf "%-6s: [%s]\n", $_, $m->{$_} for (qw(indent type key val));
print "\n";
}
}
__DATA__
some arbitrary text lines
or empty lines
could be indented
and could contain any character
+ VAR name1 = var indented by two spaces and the first nonspace character is '+'
line of arbitrary text
+ VAR name2 = var indented by 2x2 spaces
+ VAR name3 = var indented by one \t
+ VAR name4 = the next line with "name5" is not valid. missing the = character, should not be matched
+ VAR name5
+ CONST name6 = the type could be VAR or CONST
+ VAR multi1 = multiline value where the continuation lines
are indented (starts with two spaces or one tab) and NOT followed by the '+'
+ VAR multi1 = multiline value
indented
+ VAR multi1 = multiline value
indented ok too
+ VAR single = this is single line
+ because this line even if it is indented, the first nonspace character is '+'
+ VAR multi2 = multiline
could be
indented
any way
and any number of times
until the first non-indented line
the following should NOT match
+ VAR some = sould not be matched, because the line isn't indented
+ VAR some = sould not be matched, because the line isn't indented at least with TWO spaces or one tab
+ SOME name = value not matched because the SOME isn't VAR or CONST
: while($text =~/
(?m) # multiline match
^ # at the start of the line
([ ]{2}|\t)+ # two spaces or tab - at least once
\+ # the '+' character
\s* # followed by any number of spaces (e.g. "+VAR" or "+ VAR" are valid)
(VAR|CONST) # the VAR or CONST
\s+ # followed at least one space (e.g. the "VAR_" should not matched)
(\w+) # the keyword
\s*=\s* # the '=' surrounded (and consumed) by any number of spaces
( # capture the whole value (as it is)
.* # any string up to end of line
(?: # followed by (non-capturing group)
\R # one line-break
^ # at the start of the line
(?>[ ]{2,}|\t+) # atomic group - at least two spaces or at least one tab
[^+] # followed by any character but '+'
.* # any string up the end of line
)* # any number of times (e.g. optionally)
)
/xg) {
push @match, { indent => $1, type => $2, key => $3, val => $4 };
}
EDIT2そして、はい、正規表現ベースのソリューション:受け入れ答えを使用して、希望のキャプチャグループを追加するには、次のようになりました34%高速です(少なくとも私のHWでは)。
申し訳ありませんが、それを見ていません。 – ClasG
あなたはあなたが**キー**と**バリューをキャプチャしたいと思う*ヒント*を試しますが、あなたの質問は**マッチ**と言います。どちらですか?それが後者なら、私はrevoがあなたに以下の答えを与えたと信じています。あなたがそれをキャプチャしたい場合、それはより複雑です... – ClasG
ああ...今あなたが受け入れたことがわかります。私の質問への答えがあります:) – ClasG