- ベストアンサー
文字列の抽出
指定したファイルの中から'<'と'>'とで囲まれた部分文字列を抽出したいのですが方法がわかりません。どのようにしたらできるでしょうか? *ファイルは制御コードが混じっているのでバイナリとして扱わないといけないかもしれません。 よろしくお願い致します。 ------------------------------------------------- open(IN, "test.dat"); open(OUT, "> out.txt"); binmode(IN); while (<IN>) { /^<(\w+)>$/; print OUT "$1\n"; } close(IN); close(OUT);
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
#3 です。 回答にsが抜けてました。 while (/<(.*?)>/g) ではなく, while (/<(.*?)>/gs)です。 ごめんなさい。 > ●対応する'<'と'>'で抽出し、ごみの'<'や'>'は無視する これは<>がネストしてしまった場合等は一番内側の<~>内にしておくという解釈でよろしいでしょうか? 抽出したい<>の中に <や>は入らないということ前提ですが、 while部分を while(/<([^<>]*)>/sg){ とするとどうでしょう sは複数行にまたがってマッチさせるためのオプションです。binmodeを使っている場合は不要なので /<([^<>]*)>/g でも大丈夫です。 ●'<'と'>'の間に'@'がある場合のみ正規なものとする でよいのなら, while (/<([^<>]*?@[^<>]*?)>/sg) とするとどうでしょう。sは上と同じ理由でbinmodeの場合は省いてもかまいません。 -------------- 前者と後者でのテスト結果も一応残しておきます。 ■ データ <aa><bb> <cccc> <ddd ddd> eee<f fff> <gggg<hhhh>iiiii<jjj@jjjj>kkkk> lllll<m<n@nn>ooo<p> >><<qqq@qq>rrr<s @sss>ttt@uu<vv@vv>wwww> ■前者 aa bb cccc ddd ddd f fff hhhh jjj@jjjj n@nn p qqq@qq s @sss vv@vv ■後者 jjj@jjjj n@nn qqq@qq s @sss vv@vv
その他の回答 (3)
- arcsin
- ベストアンサー率70% (28/40)
自分ならこうでしょうか。 #2さんの変数に一度に読むテクを借りて。 undef($/); open(IN, "test.dat"); $_ = <IN>; open(OUT, "> out.txt"); while (/<(.*?)>/g) { print OUT "$1\n"; } close(IN); close(OUT); $/='\n'; #一応元に戻しておく .*? の?は最小マッチです。?がないと、最大マッチとなってしまうので?が必要となります。
- twinkleluz
- ベストアンサー率52% (98/185)
質問者さんのソースコードでは、以下のような問題があります。 ・ファイルを一行ずつ読んでいるので、複数行にまたがる"<"と">"の間の文字列抽出ができない 行ごとの抽出なら問題ありませんが、複数行をまたがる抽出なら、変数にファイルの内容を一度に読み込む必要があります。 Perlには読み込む時の「レコード区切り文字」が変数$/で指定されています。 デフォルトは改行が使われていますが、これを無効にすることでファイルの内容を一度に読むことができます。 undef($/); ・マッチングに使っている正規表現の誤り /^<(\w+)>$/ これは、「変数の中身が'<'で始まり、その中の文字がすべて半角英数文字で、変数の一番最後が'>'で終わる」ということを表します。 ファイルの中身が'<'で始まり、'>'で終わっていなければならないので、文字列中の'<'と'>'で囲まれた文字は抽出できません。 また、'<''>'の間に制御文字が入っていた場合も抽出できません。 また、\wでは制御コードをマッチングさせることができません。 正しくは、 /<(.+)>/s でいいと思います。 '.'は改行コードを除くすべての文字にマッチします(制御コード含む) /sをつけているのは、'.'に改行コードをマッチさせるためです。 なお、制御コードが0x00-0x7Fの間なら、binmodeを使う必要はありません。 修正後のソースは以下のような感じになります。 ------------------------------------------ undef($/); open(IN, "test.dat"); open(OUT, "> out.txt"); while (<IN>) { /<(.+)>/s; print OUT "$1\n"; } close(IN); close(OUT);
- noboru2000
- ベストアンサー率33% (47/140)
ファイルを全部読んでしまえばいいんじゃないでしょうか? (<> で読むと改行コードで区切れてしまいます)。それから <[^>]*> のパターンで中味を全部取り出せばいいと思います。 例) @data に < > で括られたデータを全て入れる。 open(F, '< ファイル名') or die; binmode(F); read(F, $buf, -s F); close(F); while ($buf =~ /<([^>]*)>/g) { push(@data, $1); }
お礼
皆様、ご回答ありがとうございます。 下記のようなデータでテストを行ったところ、下記のような出力結果となりました。 ※No.1ソースは下記のようにさせていただきました。 open(IN, '< in.txt') or die; open(OUT, "> out.txt"); binmode(IN); read(IN, $buf, -s IN); while ($buf =~ /<([^>]*)>/g) { push(@data, "$1\n"); } print OUT @data; close(IN); close(OUT); ●テストデータ ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ abcdef<zzz>abcdef<zzzz>abcdef zzzz<zzzzz>zzzzzzzz<zzzzz>zzzzzzz 01234567<z z>01234567 ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ ●No.1出力結果 ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ zzz zzzz zzzzz zzzzz z z ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ ●No.2出力結果 ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ zzz>abcdef<zzzz>abcdef zzzz<zzzzz>zzzzzzzz<zzzzz>zzzzzzz 01234567<z z ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ ●No.3出力結果 ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ zzz zzzz zzzzz zzzzz ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ 正確なテストデータをお出ししていなかったため、 皆様異なる条件を設定していただいためか、 出力結果は異なる結果となりました。 No.1様が正確な結果となりました。 条件公開があまく申し訳ございませんでした。 皆様ありがとうございます。 ところで、本当のデータの中にはごみの'<'や'>'も含まれていました。 2パターンの条件を追加したいのですが、やはりどうしてもうまく行きません ●対応する'<'と'>'で抽出し、ごみの'<'や'>'は無視する ○"dafdsa<gfdsg<0000<111>dfa<1111>dsafs>d<d" ⇒ "111"と"1111"のみ抽出 上記が難しいなら ●'<'と'>'の間に'@'がある場合のみ正規なものとする ○"dsafdsa<asf<111@111>afdsafe<> >><<11@11>asdf<a" ⇒ "111@111"と"11@11"のみ抽出 でも構いません。 申し訳ございませんが、お力添えいただけないでしょうか? よろしくお願い致します。 twinkleluz様、詳細にご丁寧にありがとうございます。徐々にですが、自分で解決する力が養われ、勉強になります。
お礼
arcsinさん、貴重なお時間をいただき、プログラムの作成、テストまで行っていただき、大変ご親切にありがとうございます。 若干データに予期せぬものがあったりしましたが、教えてもらった後、改良することで、自力解決できました。大変助かりました。ありがとうございます。