電子書籍の厳選無料作品が豊富!

下記のようなプログラムを作成しました。
(Perl のバージョンは 5.8.8 となります。)

for ($i=0; $i<=105; $i++) {
$str1 = "str1";
$str2 = "1234567890str1test";
$str2=~ s/^(\d+)($str1)/test/g;

print $i."\n";
print $1."\n";
print $2."\n";
print $str2."\n";
}

このプログラムを実行しますと、ループが100回実施するまでは $1, $2の値を
取得できるのですが、100回を超えると取得できなくなります。
(上記で行っている置換処理は100回を超えても正常に処理されます。)

質問としましては、この現象は Perl のバグなのでしょうか。
それとも、私の正規表現の書き方に不備があるのでしょうか。

ネットでいろいろ調べてみたのですが、答えが見つからなかったため、
質問させていただきました。
よろしくお願い致します。

A 回答 (6件)

for ($i=0; $i<=105; $i++) {


$str1 = "str1";
$str2 = "1234567890str1testtest";
$str2 =~ s/^(\d+)($str1)/test/g;

print $i."\n";
print $1."\n";
print $2."\n";
print $str2."\n";
}

手元に v5.005_53 があるので試したところ、ループの1回目から $1, $2 は取得できないようです。^ と /g の併用は通常意味がないので、考えられていなかったのではないでしょうか。むろん、/mg とすると $1, $2 を取得することができます。

v5.8.x の段階は、直している (仕様変更?) 途中だとも考えることができると思います。質問者のように、気づく方がいるとは予想外でしょうが...。
    • good
    • 0

また間違えた。



パターンAは4文字、パターンBは5文字残っています。

パターンAは4文字、パターンBは8文字残っています。
    • good
    • 0

自分の回答をちょっと修正


> my $str1 = "str1";
> my $str2 = "1234567890str1testtest";
> $str2=~ s/^(\d+)($str1)/test/;

s/^(\d+)($str1)/test/;

s/^(\d+)($str1)/test/g;

3) "1234567890str1test"では$1と$2に値が入り、"1234567890str1testtest"では途中から入らなくなるか
これもよくわかっていないので、参考程度にして下さい。

はじめの方への補足で
> <誤>
> $str2 = "1234567890str1test";
> ↓
> <正>
> $str2 = "1234567890str1testtest";
とのことなので、なぜ2つで違いがでるかおもしろいです。

パターンA: "1234567890str1test"
パターンB: "1234567890str1testtest"

二回目のマッチ対象文字列
パターンA: "test"
パターンB: "testtest"

パターンAは4文字、パターンBは5文字残っています。正規表現/\d+str1/は最低でも5文字必要です。したがってパターンAでは二回目のマッチをせずに終了し、パターンBでは二回目のマッチを(途中から)試行するようになるみたいです。

---
なお、質問者さんの現象は、perl v5.8.0では再現し、perl v5.10.1だと再現しませんでした。

この回答への補足

_--_1l1_1_様

ご回答いただきありがとうございます。

_--_1l1_1_様がNo.2の2) なぜ途中から結果が変ったのかで記載されていた内容について
そういう動きをするんだ。と驚きました。

perl v5.8.0と perl v5.10.1の間で、なにかあったのかもしれません。
リリースノートを確認してみたいと思います。

また、二回目のマッチ対象文字列
パターンA: "test"
パターンB: "testtest"
それぞれの動きの違いについて、_--_1l1_1_様が上記で記載されている
仕組みがあったんですね。

補足日時:2011/12/13 00:05
    • good
    • 0

しばらく更新してないActivePerl 5.10.0だけど


まったく問題なく動きます.
NO.1さんと同じ意見です.

ソースを簡略化する時点で何か間違った(実際$str2がちがってたようだし・・
けど,その違いは実行に影響がないと思う)のかも.

バージョンをあげてみるとかやることはあると思う.
もう一つ,ほとんど可能性はないとは思うけど
どのOSだとかの実行環境を考えるとか,
意表ついて実は5.8.8の不具合の可能性もまったくないわけではないから
次のバージョン(5.8.9が5.8系の最後だと思う)リリースノートをみてみるとか.

#あー5.14がでてるんだ・・一気に更新しようかな

ソースを見る限り,おかしなところはないように思うし
s///のgも問題ないはず.
一回目のマッチで$1,$2が定義されるけど
それ以上マッチしないから,$1,$2が上書きされることはないです.

あんまりdebuggerわからないけど
use re 'debug' をつけて
perl -dで実行させたら,正規表現のところで

コンパイル時には

Compiling REx "^(\d+)(str1)"
synthetic stclass "ANYOF[0-9{unicode_all}]".
Final program:
1: BOL (2)
2: OPEN1 (4)
4: PLUS (6)
5: DIGIT (0)
6: CLOSE1 (8)
8: OPEN2 (10)
10: EXACT <str1> (12)
12: CLOSE2 (14)
14: END (0)
floating "str1" at 1..2147483647 (checking floating) stclass ANYOF[0-9{unicode_all}] anchored(BOL) minlen 5

でて,実行時には105回ほど

Guessing start of match in sv for REx "^(\d+)(str1)" against "1234567890str"
Found floating substr "str1" at offset 10...
start_shift: 1 check_at: 10 s: 0 endpos: 10
Does not contradict STCLASS...
Guessed: match at offset 0
Matching REx "^(\d+)(str1)" against "1234567890str1test"
0 <> <1234567890> | 1:BOL(2)
0 <> <1234567890> | 2:OPEN1(4)
0 <> <1234567890> | 4:PLUS(6)
DIGIT can match 10 times out of 214748364
10 <67890> <str1test> | 6: CLOSE1(8)
10 <67890> <str1test> | 8: OPEN2(10)
10 <67890> <str1test> | 10: EXACT <str1>(12)
14 <7890str1> <test> | 12: CLOSE2(14)
14 <7890str1> <test> | 14: END(0)
Match successful!
Matching REx "^(\d+)(str1)" against "test"
String too short [regexec_flags]...
Match failed

こんな風にでてくるから
マッチは期待通りにできてるし,$1,$2の取得も期待通りできてます.
だいたい想像できると思いますが
BOL は ^ (Biginning Of Line)
OPEN1 は最初のキャプチャの (
PLUS は +
でCOLSE1が最初のキャプチャの ) です
BOL(2)ってのは,正規表現の「二文字目」 /が1で^が2と数えてます.

私としては・・・むしろ NO.2氏が現象を再現できたのかなと思ってます.
再現できているなら,何か内部的な問題がある(あった)のかもしれません.

この回答への補足

kabaokaba様

ご回答いただきありがとうございます。

デバッガを入れると、kabaokaba様が記載されているように
出力されるんですね。
詳しい解説ありがとうございます。

こちらの方でver5.10.0でやってみましたところ、正常に動作しました。
kabaokaba様が指摘されているように、ver5.8.8あたりにバグがあるのかもしれません。

リリースノートを確認してみたいと思います。

補足日時:2011/12/12 23:53
    • good
    • 0

1) s/^(\d+)($str1)/test/g 実行後の$1と$2は不定


my $str1 = "str1";
my $str2 = "1234567890str1testtest";
$str2=~ s/^(\d+)($str1)/test/;
これを実行するとgオプションがついているので

一回目のマッチ
対象 : "1234567890str1testtest"
$1 = "1234567890"
$2 = "str1"
結果 : 一致
二回目のマッチ
対象 : "testtest"
$1 = ??
$2 = ??
結果 : 不一致
したがって、s/^(\d+)($str1)/test/g 実行後の$1と$2は不定になります。試しにgオプションをはずしてみて下さい。上手くいきます。

2) なぜ途中から結果が変ったのか
この例だと途中から処理の仕方が違うようです。すごい適当な解説な上、私もわかっていないので参考程度にして下さい。

前半
まず、"str1”が"1234567890str1testtest"の最後に含まれる場所をsubstrで調べます。これにより、、1) の二回目のマッチが実行する前から失敗するのがわかるので、二回目のマッチ自体をやろうとしなくなります。したがって、
$1 = "1234567890"
$2 = "str1"
が残ります。

後半
どうやら後半からは、substrで調べるのは無駄と判断したのか、やろうとしなくります。1) の二回目のマッチをやろうとするため、$1と$2が空になります。
$1 = undef
$2 = undef
    • good
    • 0

コードに問題はありません。


こちらでそのコードのみを走らせたところ、
105
1234567890
str1
testtest
まできちんと出力されています。
よって原因はほかのところです。

この回答への補足

tk-is-pg_1206 様

回答いただきありがとうございます。
回答いただいたあとの報告で申し訳ないのですが、
質問で記入しましたコードに間違いがありました。
箇所としては以下の部分です。

<誤>
$str2 = "1234567890str1test";

<正>
$str2 = "1234567890str1testtest";

上記の<正>の場合でも、正常に動きますでしょうか。

また、回答いただいた内容で
>原因はほかのところです。
に関しまして、考えられる原因としてはどのようなものがありますでしょうか。

補足日時:2011/12/10 01:06
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!