• ベストアンサー
  • 困ってます

改行を正規表現での置換のあとで元に戻す。

  • 質問No.2285932
  • 閲覧数581
  • ありがとう数4
  • 回答数9

お礼率 34% (11/32)

あるテキストデータを一行ごとに読み込んで、chompで改行をはずして結合し、一つの長い行にしてから、正規表現で文章中の単語を(かなり多く)置換したとします。置換したあとのデータを、元データと同じ改行位置で改行をしたいのですが、なにか簡単な方法がありましたら教えて下さい。

質問者が選んだベストアンサー

  • 回答No.8
  • ベストアンサー

ベストアンサー率 58% (18/31)

>MeCabモジュールは入ったみたいですが、関数を使用するとエラーが出てしまいます。

my $mecab= new MeCab::Tagger();
のところで、本当に $mecab オブジェクトができたか (undef ではないか)確かめて下さい。もし undef なら、(Windows 環境では)いろいろと原因が考えられ、調べるのも厄介である可能性があります。

どの程度の規模の仕事なのかにもよりますが、こうした作業には Linux をおすすめします。

Windows しか使えない状況でも、mecab 単体なら動くと期待されますので、
mecab -O wakati in.txt > out.txt
として mecab で空白で分かち書きし、その結果を回答5のようにして(あたかも英文のように扱って)処理すればいいかと思います。但し、元ファイルに既に空白があり、且つその有無が有意味の場合は厄介ですが。

もう一つ、mecab は、文字コードに関して余り融通がきかないので、全部 EUC-JP に揃えるとか、あらかじめ注意しておくことです。
補足コメント
jukimoto

お礼率 34% (11/32)

返答が遅れて申し訳ありません。
Windows用mecabをインストールしなおしたらエラーは出なくなったのですが、今度は出力結果が駄目です(通常の形態素解析結果すらうまく出力されない)。

また、
mecab -O wakati lisk.txt > out.txt
も試したのですが、
tagger.cpp(146) [writer_.open(param)] writer.cpp(62) [! std::string(param.getProfileString(nfk.c_str())).empty()] unknown format type [lisk.txt]
といったエラーが出てしました。
ちなみに、
mecab lisk.txt -o out.txt
は大丈夫でした。

どつぼです。出来るのと出来ないのが混在して、何が原因か特定ができません。
投稿日時:2006/07/24 11:17

その他の回答 (全8件)

  • 回答No.9

ベストアンサー率 58% (18/31)

>mecab lisk.txt -o out.txt
>は大丈夫でした。

それなら、その各行の最左端の語を空白で切って連ね、EOS が来たら改行すれば、空白で分かち書きしたテキストができるわけですから、後は英語などと同じようにして処理できるかと思います。

mecab は、エラーを起こした場合の原因が探りにくいときがあるので、それなりの規模のテキストの処理をしているのであれば、windows より Linux などの環境をおすすめしますが。
  • 回答No.7

ベストアンサー率 58% (18/31)

>日本語の単語を抽出してタグ付きの単語に置換する作業を行っています。

それでしたら、回答5の最後に書いたように
>元のデータが分かち書きされていないのであれば、mecab などで単語切りをしてやらないといけません

ということになります。具体的には、まず MeCab で単語を取り出しておいて、それぞれの単語に置換を掛ければいいかと。

--------------------------
use strict;
use MeCab;

my @word_list= get_word();
foreach my $word (@word_list) {
$word=~ s/FROM/TO/;
...
print $word;
}

sub get_word {
my @rec_list= map { chomp($_); $_; } <>;
my @len_list= map { length($_) } @rec_list; # keep original length

my @out_word_list;
my $out_len= 0;
my $in_len= shift(@len_list);

my $mecab= new MeCab::Tagger();
my $token= $mecab->parseToNode(join('',@rec_list));
while($token) {
my $word= $token->{surface};
push(@out_word_list,$word);
if ($out_len + length($word) >= $in_len) {
push(@out_word_list,"\n");
$in_len+= shift(@len_list);
}
$out_len+= length($word);
$token= $token->{next};
}
shift(@out_word_list); #cut BOS
pop(@out_word_list); #cut EOS
return @out_word_list;
}
----------
なお、置換リスト中の「置換前」の語を MeCab の辞書に登録しておかないと、MeCab が誤認したり切り過ぎてしまって失敗します。
たとえば「年末謝恩大売出し」を置換対象にしたいなら、それを辞書に登録しておかないと、MeCab が「年末/謝恩/大/売出し」のように切ってしまうかもしれません。
補足コメント
jukimoto

お礼率 34% (11/32)

細かい説明ありがとうございます。
実はperlにあるまじきWindows環境なのですが、以下の参考URLの通りしたのですがMeCabモジュールは入ったみたいですが、関数を使用するとエラーが出てしまいます。
参考URL:http://namazu.asablo.jp/blog/2006/04/04/315580
投稿日時:2006/07/21 14:32
  • 回答No.6

ベストアンサー率 41% (7/17)

改行を無視して置換したいってことですよね?

「test」を「a_test_b」に変換
$body = "testtes\ntte\nst\n";

$body =~ s/(t[\n]*e[\n]*s[\n]*t)/a_$1_b/;

でいいんじゃないですか?

chompが具体的にどうやって正規表現でおきかえることができるか確認してないので、そのへんは調整必要ですが。
  • 回答No.5

ベストアンサー率 58% (18/31)

改行を削除したものと削除しないものの両方に置換を掛け、双方の差が生じた場合に改行削除の方を使って出力する、という方法でいいと思います。

ただ、仕様に不明な点が多い。一番の問題は、改行部分に置換が生じたとき、元の改行位置をどこまで保存したいかです。例えば、「abc」→「ABC」という置換ルールのとき、「ab[改行]c」は「AB[改行]C」に、「a[改行]bc」は「A[改行]BC」にしたい、とかになると結構厄介。なぜなら、「longlongword」→「SHORT」というルールで、「longlong[改行]word」が入力されたとき、「SHORT」のどこで改行するのが正しいか分りませんので。

こうした問題はなく、改行を含む置換では置換した語の直後に改行すればよし、というのであれば、次のような形かと。
--------
use strict;
my $WORD_DIVIDER= ' ';

my $rec= join('',<>);
my $outrec= split_word($rec);
print join($WORD_DIVIDER,@$outrec);

sub split_word {
my($rec)= @_;
my $outarray= [];
foreach my $word (split(/[ ]/o,$rec)) {
my $word_strip= $word;
$word_strip=~ s/\-?[\r\n]//go;
my $x_word= change_word($word);
my $x_word_strip= change_word($word_strip);
if ($x_word_strip eq $word_strip) {# no change
push(@$outarray,$word);
} elsif ($x_word_strip eq $x_word) {# change not affected by LF
push(@$outarray,$x_word);
} else {
push(@$outarray,$x_word_strip . "\n");
}
}
return $outarray;
}

sub change_word {
my($word)= @_;
# just testing
$word=~ s/abc/ABC/g;
$word=~ s/hij/HIJ/g;
$word=~ s/rst/RST/g;
$word=~ s/xyz/XYZ/g;
$word=~ s/Supercalifragilisticexpialidocious/LONG/g;
return $word;
}
---------
test data
test a-
bc def hij
klm Super-
califragilistic-
expialidoc-
ious opq rs-
t uvw x-
yz done
-------------
出力
test ABC
def HIJ
klm LONG
opq RST
uvw XYZ
done
---------
元のデータが分かち書きされていないのであれば、mecab などで単語切りをしてやらないといけません。
補足コメント
jukimoto

お礼率 34% (11/32)

詳しい内容ありがとうございました。
残念ながら日本語の置換なので空白の分かち書きはされていません。

詳しく書くと、日本語の単語を抽出してタグ付きの単語に置換する作業を行っています。

置換前:日本語
置換後:<FONT color=\"#FF0000\">日本語</FONT>

といった感じです。改行は置換された単語の直後にされる仕様で問題ありません。
投稿日時:2006/07/20 09:35
  • 回答No.4

ベストアンサー率 62% (800/1280)

元データの整形(されているのなら)のルールというのは決まっていないんですか?
もし決まっているなら、改行を取り除いて置換を行った後、再度そのルールに
従って整形してやるのが一番手っ取り早いと思うんですが。

また、置換の結果文字数が変わりうるのであれば「元データと同じ改行位置」
というのはちょっと無理があると思います。
  • 回答No.3

ベストアンサー率 40% (148/365)

置き換えで文字数が変わってしまうとダメと言うことは・・・
置き換えで文字数が変わらないようにすればOKですよね

置き換え前:いろはにほへと
置き換え後:ABC
の場合はABCをあらかじめ"ABC@@@@@@"と補完して置き換え後、@を消す(@にはめったに使わない文字や記号を使用します)

置き換え前:ABC
置き換え後:いろはにほへと
の場合は、"いろはにほへと"を"@1****"などとタグ+長さ補完の値で置き換え、後で1つずつ置き換えていく・・・
・・・面倒そうですがどうでしょうか?
  • 回答No.2

ベストアンサー率 40% (148/365)

1行毎に読み込む際、行の文字数を配列で保存して分割する際にそれを利用する
(置き換えで文字数が変わってしまうと駄目ですが)
補足コメント
jukimoto

お礼率 34% (11/32)

すいません、説明がたりませんでした。
重要なのは改行にまたがった単語を正規表現で検出したいので一度改行を外す必要がある訳です。ついでに置換後は文字数は変化する仕様になっています。
投稿日時:2006/07/19 18:33
  • 回答No.1

ベストアンサー率 19% (178/917)

最初に改行を「トル」のではなく,めったに使わない文字や記号に変換しておき,最後に改行に置換するといいです.
〃や仝など.
補足コメント
jukimoto

お礼率 34% (11/32)

すいません、説明がたりませんでした。
重要なのは改行にまたがった単語を検出したいので一度改行を外す必要がある訳です。ですから違う記号にしおくと単語が検出できません・・・。
投稿日時:2006/07/19 18:37
関連するQ&A

その他の関連するQ&Aをキーワードで探す

ピックアップ

ページ先頭へ