• ベストアンサー

文字列の抽出

指定したファイルの中から'<'と'>'とで囲まれた部分文字列を抽出したいのですが方法がわかりません。どのようにしたらできるでしょうか? *ファイルは制御コードが混じっているのでバイナリとして扱わないといけないかもしれません。 よろしくお願い致します。 ------------------------------------------------- open(IN, "test.dat"); open(OUT, "> out.txt"); binmode(IN); while (<IN>) { /^<(\w+)>$/; print OUT "$1\n"; } close(IN); close(OUT);

noname#27709
noname#27709
  • Perl
  • 回答数4
  • ありがとう数3

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

  • ベストアンサー
  • arcsin
  • ベストアンサー率70% (28/40)
回答No.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

noname#27709
質問者

お礼

arcsinさん、貴重なお時間をいただき、プログラムの作成、テストまで行っていただき、大変ご親切にありがとうございます。 若干データに予期せぬものがあったりしましたが、教えてもらった後、改良することで、自力解決できました。大変助かりました。ありがとうございます。

その他の回答 (3)

  • arcsin
  • ベストアンサー率70% (28/40)
回答No.3

自分ならこうでしょうか。 #2さんの変数に一度に読むテクを借りて。 undef($/); open(IN, "test.dat"); $_ = <IN>; open(OUT, "> out.txt"); while (/<(.*?)>/g) { print OUT "$1\n"; } close(IN); close(OUT); $/='\n'; #一応元に戻しておく .*? の?は最小マッチです。?がないと、最大マッチとなってしまうので?が必要となります。

回答No.2

質問者さんのソースコードでは、以下のような問題があります。 ・ファイルを一行ずつ読んでいるので、複数行にまたがる"<"と">"の間の文字列抽出ができない 行ごとの抽出なら問題ありませんが、複数行をまたがる抽出なら、変数にファイルの内容を一度に読み込む必要があります。 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);

回答No.1

ファイルを全部読んでしまえばいいんじゃないでしょうか? (<> で読むと改行コードで区切れてしまいます)。それから <[^>]*> のパターンで中味を全部取り出せばいいと思います。 例) @data に < > で括られたデータを全て入れる。 open(F, '< ファイル名') or die; binmode(F); read(F, $buf, -s F); close(F); while ($buf =~ /<([^>]*)>/g) {  push(@data, $1); }

noname#27709
質問者

お礼

皆様、ご回答ありがとうございます。 下記のようなデータでテストを行ったところ、下記のような出力結果となりました。 ※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様、詳細にご丁寧にありがとうございます。徐々にですが、自分で解決する力が養われ、勉強になります。

関連するQ&A

  • 文字列の抽出がうまくできません

    こんにちは。Perl初心者です。みなさんのお力をお借りしたく、初めて投稿させていただきました。よろしくお願いします。 現在、ファイルから特定した行を抽出し、その行に含まれる文字列を抽出しようとしているのですが、その文字列の抽出がうまくできません。 <文字列を含むファイル:xxx.txt> -------------------------- To 鈴木 太郎さん 1行目 : 01,りんご, 3 個, ナシ, 1個 14行目 02,キャベツ, 3 個, なす, 10本 15行目 : -------------------------- <スクリプトファイル:yyy.pl> -------------------------- open(DAT, "./xxx.txt") || die("can't open file : ($!)"); @file = <DAT>; close DAT; if ( $file[0] =~ /(\W+\s)(\W+)/ ) { print "$1\n"; } print "$file[13]\n"; print "$file[14]\n"; -------------------------- <結果> -------------------------- 鈴木 -------------------------- 抽出したい結果は「鈴木 太郎」なんですが、上記のような結果(「1byteのスペース」鈴木「2byteのスペース」)になってしまいます。xxx.txtの「To」と「鈴木」の間と、「鈴木」と「太郎」の間にはそれぞれ1byteのスペースがあります。どうやったら「鈴木 太郎」と抽出できるか教えてください。 スクリプトは「Copal2(ver2.77)」というツールを使って作りました。そのツールではエンコードは「自動判別」となっており実際何が使われているのか分かりません。 また他にも質問があります。 今抽出したい行を指定して出力させてますが、 「To」から始まる行 「01」といった2桁の数字から始まる行 というような抽出の仕方をしたいのですが可能でしょうか? 本当に初心で大変恐縮ではありますが、教えてください。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • バイナリファイルの出力

    「test.dat」というファイルをブラウザから ダウロードさせるためにバイナリ出力をしたいのですが、 空ファイルばかりできて上手くいきません。 以下のようなコードなのですが、どこが悪いでしょうか? ご教授ください。お願いします。 症状:ブラウザからダウンロードすると、ファイル名は正しいが 空のファイルになっている 環境:perl 5.8.7 windows server2003 ----------------------------------------------------------- #!/usr/local/bin/perl print "Content-Disposition: attachment; filename=\"test.dat\"\n"; print "Content-Type: application/octet-stream\n\n"; open(IN, "test.dat"); binmode(IN); binmode(STDOUT); print while (<IN>); close(IN); -----------------------------------------------------------

    • ベストアンサー
    • Perl
  • 初心者で、困っています。(文字化け)

    すみません、すごく初歩的なことで困っています。 本を見ながら、下のようなものを書いたのですが、 出力すると、文字化けをしてしまいます。ウィンドウズの環境で、Perlは5.8を使い、読み込みのテキストはUnicodeです。どのようにすれば、文字化けをしないで、読み込めるのでしょうか。 open ( IN , "LBa3_00028.txt" ) or die; open ( out , ">out.txt" ); while ( $line = <IN>){ print out "$line\n"; } close(out); close(IN);

    • ベストアンサー
    • Perl
  • データからある文字列の次の行を出力するには

    perlの初心者です。はじめまして。 質問タイトルそのままなのですが、データからある文字列の次の行を出力するにはどのような記述をすればよろしいでしょうか?よろしくお願いします。 ・データは1ファイルで2000あります。1つのデータは[X,Y]で始まり、次の[X,Y]の前までになります。 ・出力はCSVファイル。 ・DAT errがあった場合はその次の行を出力。なかったら、DAT列は空白にする。 #!/bin/perl open(FILE,"<$ARGV[0]"); open(OUT,">$ARGV[0].csv"); ########################## err count ########################### print OUT "X,Y,NUMBER,DAT,\n"; while(<LOG>){ if($_ =~ /X=(.+)\,Y=(.+)/){ chomp($_); print OUT "\n$1,$2,"; } if($_ =~ /NUMBER (.+)/){ print OUT "$1,"; } if($_ =~ /DAT err/){ print OUT "PASS,0,"; } } close(OUT);

    • ベストアンサー
    • Perl
  • perlで、文字列の中から何番目から何番目までの文字を抜き出したい

    perl初心者です。 文字列の中から何番目から何番目までの文字を抜き出して 別のファイルにうつす、 というスクリプトを書いています。 でも、うまくうごいてくれません。。。 恐らく、根本的におかしな部分があると思いますし、 さらに、何番目から、は指定できていても、 次の数字は文字数を意味しているのだと思いますので、 意図するものと違う気がします。 ただ、何番目から何番目 という情報だけを入力してできたら作業上大変助かります。 本当に困っております。 アドバイスをいただけると幸いです。 大変急いでおります(;;)。 宜しくお願いします!!! #!/usr/bin/perl ; open(IN, "test.txt") or die ; open(OUT, ">after_test"); while(<IN>) { chomp ; if (/(\S+)/) { \S = $S substr ($S, 12345, 12389) = { print OUT "$_\n"; } } } close (IN) ; close (OUT) ;

    • ベストアンサー
    • Perl
  • Perlを使って、大文字小文字関係なく、重複行を削除したい。

    現在、下記のコードで重複行を削除し、ファイルを作成しているのですが、 AAA aaa AaA などのように大文字、小文字が混じっている場合は重複とはみなさず削除の対象になりません。 こういった場合も重複とみなして削除させたいのですがどのようにすればいいでしょうか? open(IN, "INfilename.txt"); open(OUT, ">Outfilename.txt"); while(){ if(!exists($count{$_})){$count{$_}++;print OUT $_;} } close (IN); close (OUT);

    • ベストアンサー
    • CGI
  • Perlを使った正規表現について

    テキストファイルを読み込んで、以下のルールに従った文字列だけを.txtで出力するプログラムを書いています。 具体的にはテキストファイル中の "text":"★★★","to_user" で囲まれている★★★の部分のみを抽出したいです。 そこで正規表現に触りだしたのですが、表現がうまくいっていないようで1週間ほどはまっています。。うまく出力できるように直しをいただけないでしょうか。 打ち明けてしまうと、★★★の部分はtwitterの呟き(日本語)になります。 以下がそのソースになります。よろしくお願いします。 print "Input file name: "; $ifname = <STDIN>; open(IN, $ifname); open(OUT, "> out.txt"); #出力ファイル while(<IN>){ if($ifname =" m/"test":(.*?),"to_user"/; next if($_ eq ""); print OUT "$_\n"; #書き出し } close(OUT); close(IN);

    • ベストアンサー
    • Perl
  • foreach構文をwhile構文で実現したい。

    下記にありますforeach構文をwhile構文で実現しようと試みているのですが 何故か同じように実現できず、無限ループになっているような気がします。 どこに問題があるのか当方ではわからない為、どなたかご教授いただけませんでしょうか。 ------------------------------ $ cat list1.txt 01<>ああああ 01_01<>あAAA 01_02<>あBBB 01_03<>あCCC 01_04<>あDDD 02<>いいいい 02_01<>いAAA 02_02<>いBBB 02_03<>いCCC 02_04<>いDDD ------------------------------ ■成功版 open(IN, "<list1.txt"); @datas = <IN>; close(IN); open(OUT, ">date.txt"); foreach (@datas) { chomp; ($a, $b) = split(/<>/, $_);#$_は省略できます。 print OUT "'$a'=>'$b',\n"; } close(OUT); ■取組版 open(IN, "<list1.txt"); @datas2 = <IN>; close(IN); open(OUT, ">date2.txt"); while (@datas2) { s/^/'/; s/<>/'=>'/; s/$/',/; print OUT; } close(OUT);

    • ベストアンサー
    • Perl
  • 置換した文字列を変数に代入する方法

    お世話になります。 たとえば、 #!C:\Perl\bin\perl print $form->header("text/html");  : open(IN,"<file.txt"); while(<IN>){ : : s/あい(.*?)うえお/$1/; (????)←「$1」の内容を変数に代入したい : : } close(IN); というように、テキストファイルの文章中の 「あい(.*?)うえお」から(.*?)を取り出して、 その取り出した(.*?)を変数に代入したいのですが、 いろいろ試してやっているのですが、難儀して おります。どのように書いたらいいか教えてください。よろしくお願いします。 最終的にclose(IN);のあとで、print 変数;として(.*?)の文字列を表示させたいです。

    • ベストアンサー
    • Perl
  • ファイルの最後の行から表示させる(最新情報を5回分だけ表示)

    ファイルの最後に1行追加し、6行以上になるとファイルの先頭を1行削除。 表示は最後の行から行うスクリプトを作りたいのです。 新しい情報を5回分だけ表示する様にしたかったのですが、余りにも力技なのでもう少しスマートにやる方法を教えてもらえませんか? ----sort.txt---- 1a 2b 3c 4d ---------------- #!/usr/bin/perl $fname = "sort.txt"; print "Content-type: text/html;\n\n"; $aaa="5e"; open(OUT, ">>$fname"); flock(OUT, LOCK_EX); print OUT "$aaa\n"; flock(OUT, LOCK_NB); close(OUT); open(IN, "$fname"); $cnt = "0"; while($line = <IN>){ $a[$cnt] = "$line"; $cnt++; } close(IN); if($cnt > 5){ open(OUT, ">$fname"); flock(OUT, LOCK_EX); $cnt = "0"; while($cnt < 6){ if($cnt > 0){ print OUT "$a[$cnt]"; } $cnt++; } flock(OUT, LOCK_NB); close(OUT); } $cnt = "0"; open(OUT, "$fname"); @buff = <OUT>; close(OUT); foreach $line ( reverse @buff ){ $cnt++; print "$line<br>\n"; } exit();

    • ベストアンサー
    • Perl

専門家に質問してみよう