
Perlスクリプトで、以下のような置換をしようとしています。
置換前 piyo … fuga … piyo 【piyo … fuga … piyo … hoge … piyo】 fuga … piyo
置換後 【ぴよ】 … fuga … 【ぴよ】 【piyo … fuga … piyo … hoge … piyo】 fuga … 【ぴよ】
【】が入れ子にならないように、【】の外の部分でだけマッチ、置換しようと正規表現を考えていたのですが、いくらやってもできません。調べていたら、Perlでは可変長の先読み戻り読みをサポートしていないらしいことがわかりました。
正規表現一つでも、複数行のスクリプトでも構いません。どのようにすればよいでしょうか。
A 回答 (6件)
- 最新から表示
- 回答順に表示
No.6
- 回答日時:
No.5 の補足に書かれたコードを見ると、open 文の引数に "<:utf8" があるのでファイルからの読み込みは「UTF-8 内部文字列」になるのはわかるのですが、/ほげ/ と @file_str が「UTF-8 内部文字列」になっているのか、それとも「UTF-8 バイト文字列」なのかは判断できません。
大丈夫でしょうか?まずは、No.5 の補足のコードがなぜうまく行かないのか、突き止めることをお奨めします。例えば、/ほげ/ が ASCII 以外の文字を含んでいて「UTF-8 バイト文字列」である場合は、if の内部に入ることはありません。if の内部に入ることが確認できたら、簡単な正規表現を実行してみるとか、いろいろとやれることはあると思います。
なお、while ループで1行ずつ読み込んで処理するには、フラグ的な変数を用いるのも一つの方法です。
my $prev = 0;
while (my $line = <FH>) {
...
if ($prev) {
foreach (@file_str) {
$line =~ ...
}
$prev = 0;
} else {
$prev = 1 if $line =~ /ほげ/;
}
...
}
この回答への補足
お話と関係のある箇所と思われる部分がわかるようにした、スクリプトのほぼ全体は、以下のとおりです。(OSはWindows、元のファイルはすべてutf8で保存してあります)
use strict;
use warnings;
use utf8;
use Encode
binmode STDIN, ':encoding(utf8)';
binmode STDOUT, ':encoding(utf8)';
binmode STDERR, ':encoding(cp932)';
# ドキュメントファイルオープン(読み込みモード)
my $document = $ARGV[0];
open (my $in,"<:utf8", $document)
or die qq/Can't open file "$document" : $!/;
# 全文を配列として入れる
our @sentence = <$in>;
# ファイルクローズ
close ($in);
# リストファイルオープン(読み込みモード)
my $list = 'list.txt';
open (our $list_in_handle,"<:utf8", $list)
or die qq/Can't open file $list : $!/;
#リストファイルの内容を配列に格納
our @list_str = <$list_in_handle>;
# リストファイルクローズ
close ($list_in_handle);
# 置換ファイル作成(追加書き込みモードでオープン)
my $filename = 'filename.txt';
open (our $out_handle,">> $filename");
# ドキュメントファイルの各行について
for (my $i=0; $i<=$#sentence; $i++) {
# 出力してから
print $out_handle Encode::encode("utf8", $sentence[$i]);
# 文字列がマッチすれば
our $hoge = 'source';
if ($sentence[$i] =~ /$hoge/) {
# 次の行を置換
foreach (@file_str){
$sentence[$i+1] =~ s/問題の$_箇所/問題の『$_』箇所/g;
}}}
# 最後に置換ファイルクローズ(略)
「$hoge」の部分は今回はascii文字列です。
ありがとうございます。
最初にいただいた正規表現でスクリプトが動くこと自体は確認しています(入れ子にはなりましたが)。
いただいたwhileのサンプルスクリプトは私の知識を超えているので、勉強させていただきます。
Perlの内部文字列とバイト文字列について、さきほども検索していましたが、なかなか理解できません。なんとかOJTで理解できるようになれればと思うのですが。
道は遠いですね。
No.5
- 回答日時:
> Complex regular subexpression recursion limit
上記のエラーは、正規表現の部分式が再帰の上限を超えた場合に出力されます。大きなファイル全体等、正規表現の対象テキストが大きすぎるのが原因だと思います。while ループで1行ずつ読み込むようにすれば、効率的に処理できるようになります。『 と 』 が行を跨いでいなければ、次のようなコードで処理できると思います。なお、プログラムと入力ファイルはともに UTF-8 と仮定しています。
use strict;
use Encode;
use utf8;
open FH, 'inputfile' or die $!;
while (my $line = <FH>) {
$line = decode 'utf8', $line;
$line =~ s/\G((?:[^『』]|『[^『』]*』)*?)piyo/$1『ぴよ』/g;
$line = encode 'utf8', $line;
print $line;
}
close FH;
正規表現の適用は、対象文字列と正規表現の両方を「UTF-8 内部文字列」にする必要があります。そうでなければ、『 や 』 が1文字として認識されません。
この回答への補足
実際の私のスクリプトは以下のように、document全体を読み込んでから、各行について「ほげ」にマッチすれば、次の行で、別リスト上の語(piyo)の置換をかけるというようなものです(初心者のため、レキシカル変数などおかしなところがあるかもしれませんが、動けばよいと考えています)。
whileでどのように自分のやりたいことを実現できるか考えたいと思います。ご教示があればよろしくおねがいします。
# ファイルオープン(読み込みモード)
my $document = $ARGV[0];
open (my $in,"<:utf8", $document)
or die qq/Can't open file "$document" : $!/;
# 全文を配列として入れる
our @sentence = <$in>;
# tmxファイルクローズ(読み込みモード)
close ($in);
for (my $i=0; $i<=$#sentence; $i++) {
if ($sentence[$i] =~ /ほげ/) {
#その次の行を置換
foreach (@file_str){
$sentence[$i+1] =~ s/問題の箇所/g;
}
}
}
No.4
- 回答日時:
この手を正規表現だけで頑張るのは感心しません
読みにくくて直しづらいコードになりがちなので
というわけで素直に複数行で解決しましょう
$s = "piyo … fuga … piyo <piyo … fuga … piyo … hoge … piyo> fuga … piyo";
$t = "";
foreach ($s =~ /[^<>]+|<.*?>/g) {
s/piyo/<PIYO>/g unless(/</);
$t .= $_;
}
print $t;
この回答への補足
全角文字『』を使用しているせいか、私の環境ではout of memoryとなってしまいました。
私の実際のスクリプトは、
foreach (@file_str){
$sentence[$i] =~ s/問題の箇所/g;
}
というようなものです。
ありがとうございました。
Perl初心者のため、いただいたスクリプトを全部理解しないまま自分の状況に置き換えて試してみましたが、our of memory になってしまいました。
No.3
- 回答日時:
No.2 さんの指摘通り、前回の解答は作りが雑で少し欠陥があります。
最後の piyo がない場合も、カッコ内の piyo を置き換えてしまいます。次のように、変更させてもらいます。$str =~ s/\G((?:[^][]*?|\[[^][]*\])*?)piyo/$1\[PIYO\]/g;
この回答への補足
自分の環境ではPerlでやることはできないのでしょうか。
可変長をサポートしている他の言語と組み合わせたり、どんな形でも実現できればよいのですが。
ありがとうございました。全角文字『』を使用しているためか、以下のようなメッセージが出てしまいました。
Complex regular subexpression recursion limit
No.1
- 回答日時:
オプションでカッコの部分を読み取ってしまえば、カッコの外側のみの piyo だけを置き換えることができると思います。
なお、次のコードは、文字コードの影響を受けないように ASCII のみを使っています。$str = 'piyo ... fuga ... piyo [piyo ... fuga ... piyo ... hoge ... piyo] fuga ... piyo';
$str =~ s/((?:\[.*?\].*?)*)piyo/$1\[PIYO\]/g;
print "$str\n";
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Windows 10 バッチファイルの記述法とルールについてアドバイスをお願いいたします。 1 2022/04/13 10:50
- Perl perlをバージョンアップしたら、今まで正常に動いていたプログラムが、エラーになってしまった 3 2022/10/05 15:44
- Perl perlで2次元配列をサブルーチンに値渡しで渡す 5 2022/12/17 18:49
- Perl Perlで特定文字列から特定文字列までを抜き出したい 4 2022/04/02 14:24
- その他(コンピューター・テクノロジー) 正規表現の置換で一部の文字列をそのまま残したい 2 2022/05/03 19:19
- JavaScript JAVASCRIPT 2 2022/04/15 15:10
- フリーソフト サクラエディタの正規表現(grep機能)の使い方 3 2022/06/22 10:29
- マウス・キーボード 何故、ローマ字や英語を全角で打つ? 5 2022/05/15 09:36
- Perl perlでリテラル値はメモリにどのように格納されているか? 1 2023/01/15 20:45
- Windows 10 Windowsのバッチファイルで正規表現の置換方法について… 4 2022/12/09 16:00
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
各項目がダブルクォーテーショ...
-
C#で空白行を削除する方法
-
csvデータのダブルクォーテーシ...
-
正規表現で、特定の文字列を含...
-
VBA 置換文字がみつからない時
-
csvデータ ダブルクォーテ...
-
秀丸で数字だけの行を削除したい
-
秀丸エディタで、「-」や「ー」...
-
Excel・ユーザーフォームの情報...
-
スペースで区切られた氏名から...
-
フォントの色を保持したままセ...
-
○文字目に文字挿入
-
MKエディタやサクラエディタを...
-
テキストボックスの文字列を置...
-
EXCEL VBA でCTRL+Fのダイア...
-
C言語でテキストファイルの内容...
-
CSVファイルの中で、「 , 」カ...
-
エクセルで数値を全角文字(カ...
-
住宅にカナを入力する際に丁目...
-
Excelについて質問です。 セル...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VBA 置換文字がみつからない時
-
正規表現で、特定の文字列を含...
-
csvデータ ダブルクォーテ...
-
各項目がダブルクォーテーショ...
-
Excel VBA リストに一致したデ...
-
スペースで区切られた氏名から...
-
秀丸エディタで、「-」や「ー」...
-
EXCELマクロを用いてグラフの系...
-
csvデータのダブルクォーテーシ...
-
C#で空白行を削除する方法
-
EXCEL警告「置換対象のデータが...
-
xmlファイル内の文字列置換
-
正規表現 特定の文字列を含む行...
-
テキストボックスの文字列を置...
-
○文字目に文字挿入
-
C言語でテキストファイルの内容...
-
複数のパワーポイントファイル...
-
c# ビルド直前にコードを置換で...
-
Excel・ユーザーフォームの情報...
-
\\(円)記号を置換したい
おすすめ情報