• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:12文字以上の単語リスト作成)

Perl初心者の方への質問:テキストファイルから12文字以上の単語を抽出する方法がわかりません

このQ&Aのポイント
  • Perl初心者の方が、テキストファイルから12文字以上の単語を抽出して新しいファイルに保存するスクリプトを作成しようとしていますが、うまくいきません。
  • 現在、カレントディレクトリにあるテキストファイルを読み込んで、12文字以上の単語を抽出する処理を行っていますが、抽出されるのは12文字以上の単語を含む行全体のようです。
  • スクリプトのどこが間違っているのか、また根本的なアプローチ自体が間違っているのかを教えていただきたいです。

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

  • ベストアンサー
  • kuroizell
  • ベストアンサー率55% (95/170)
回答No.3

>>3) そのファイルを一行ずつ処理する。 >このあたりから、苦しくなってきます。 という事で、ここの部分から貼っておきます。 適当に書き換えてください。 my %over12; foreach my $line(@contents) { my @words = split /\s/, $line; foreach my $word(@words) { $word =~ s/[\W]//g; next unless $word =~ /\w{12,}/; $over12{$word} = $word; } } foreach my $key (keys %over12) { print "$key\n"; }

Kazu_creator
質問者

お礼

ご回答ありがとうございます。 お陰さまで、意図したとおり処理が出来ました。 「ファイルを1つずつ開く」ところまではわかると言いながら、上記の回答とファイルを開く部分を合体するだけのことにずいぶん苦労してしまいました。 でも、いろいろいじくりまわしている内に、内側の繰り返しで宣言した変数や配列(my $abc, my @efgなど)は外側に持ち越せないというルールがあるらしいということに気付きました。 入門書レベルだと、そういう細かいことは書いてないんですよね。 早く応用編に進みたいです。 use strict; use warnings; my %over12; while ( my $file = glob '*.txt' ) { #1つ目の繰り返し処理開始 open my $reading, '<', $file or die; my @contents = <$reading>; close $reading; foreach my $line(@contents) { #2つ目の繰り返し処理開始 my @words = split ' ', $line; #@wordsは2つ目の繰り返しの中で宣言しているから外側では使えない??? foreach my $word(@words) { #3つ目の繰り返し処理開始 $word =~ s/[\W]//g; next unless $word =~ /\w{12,}/; $over12{$word} = $word; } #3つ目の繰り返し終わり } #2つ目の繰り返し終わり foreach my $key (keys %over12) { #4つ目の繰り返し開始 print "$key\n"; } #4つ目の繰り返し終わり } #1つ目の繰り返し終わり

その他の回答 (5)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.6

12文字以上じゃなくて12文字だけというのはわかりませんが、 改行がなくて全部が一行にまとまってしまうというのは、 print NEWFILE @results; のように書くと、確かに @results の要素をすべて出力はするのですが、 一要素ごとに改行を自動でつけるということはしません。 自分でつけて回らなければいけないということで、 print NEWFILE join("\n", @results); のようにしてみてください。

Kazu_creator
質問者

お礼

度々のご回答ありがとうございます。 なるほど、そういうことだったんですね。 print NEWFILE @results;にjoinを入れ子にできるというのもしりませんでした。勉強になります。 「12文字の文字のみ」というのは私の勘違いで、13文字の単語もヒットしていました。ただし、「japaneseeeeeeeeeeeee」という意図的に長くした単語だけはヒットしないんですよね。print NEWFILE join("\n", @results);とした時だけ…。 foreach my $result (@results) { print "$result\n"; とすれば、ちゃんと表示されるのに何故そうなるのか、いまの私のレベルでは見当もつきません。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.5

すみません。なにか盛大に思い込みが入ってました。 一行で書くのなら my @results = grep(/[a-zA-Z']{12,}/,@content); の変型は my @results = grep(/[a-zA-Z']{12,}/, map {split ' '} @content); といった感じですね。 さすがにこれはやり過ぎというところなので、 foreach my $line (@content) { my @t = split ' ', $line; my @result = grep(/[a-zA-Z']{12,}/, @t); foreach my $result (@results) { (省略) } } としたほうが分かりやすいでしょうか (@tや@resultを使わないようにも書けますが)。 あと、split の第一引数ですが、' ' や/ / のように半角スペース 一個のときは特別な動作をして、半角スペース一個で分割するわけではありません。 \s+を使ったときと概ね同じです。 どうもこれをご存じない方が少なくないようなのでお節介ながら 書いておきます。 それと、ファイルをオープンしたときのファイルハンドルに NEWFILEのような大文字のbareword を使うのは今は推奨されていません。

Kazu_creator
質問者

お礼

ありがとうございました。 以下のようなかたちにして、なんとか目的を遂げることができました。 while ( my $file = glob '*.txt' ) { open my $reading, '<', $file or die; my @content = <$reading>; close $reading; foreach my $line (@content) { my @t = split ' ', $line; my @results = grep(/[a-zA-Z']{12,}/, @t); foreach my $result (@results) { print "$result\n"; } } } これを、コマンドプロンプトで「perl filename.pl >output_file.txt」のように打ち込んで出力すると、最終成果物のリストができます。 しかし、テキストファイル作成まで自動化しようとして最後の部分を foreach my $result (@results) { open (NEWFILE, ">output_file.txt") or die "$!"; print NEWFILE @results; close(NEWFILE); } } } とすると、なぜか(1)単語が改行なしでつながってしまう(2)12文字以上ではなく12文字の単語のみが拾われている、という問題が発生してしまい、これの原因がよくわかりません。 きっとまた、私がバカなことをしているのだと思うのですが、余裕があったら何を間違えているのか、ご指摘いただけると助かります。

  • ryu_chan
  • ベストアンサー率37% (69/186)
回答No.4

やり方は色々あると思いますが、ハイフネーション処理を簡単にするためにスカラー変数に全行を一挙に読み込みました。 ハイフネーションを考慮する必要がなければ、下で回答されている方々の方法を取ればいいと思います。 my %long_word; while ( my $file = glob '*.txt' ) { open my $reading, '<', $file or die $!; my $sentences = do { local $/; <$reading> }; close $reading; while ( $sentences =~ /([a-zA-Z']+(-\n[a-zA-Z']+)*)/g ) { my $word = $1; $word =~ tr/-\n//d; $long_word{$word}++ if length $word >= 12; } } open my $out, ">LongWords_12.txt" or die $!; print {$out} join "\n", keys %long_word; close $out;

Kazu_creator
質問者

補足

ご回答ありがとうございます。 上記のリストで目的の処理ができることは確認させてていただきましたが、「do { local $/; <$reading> };」や「$long_word{$word}++ if length $word >」など何をやっているのかわからない部分が多く、現在調べているところです。 もう少しわかったら、もっとまともなお礼コメントをさせていただこうかと思っています。決して「答えさえわかれば後はどうでもいい」と思っているわけではないのでご理解ください。 実は、皆さんの回答を参考にドイツ語、フランス語で同様の処理をしたいと思っています。 その場合「[a-zA-Z']{12,}」という正規表現では欧州文字に対応できません。現在はテキストエディタでまず「[a-zA-Z]*'*\w{12,}」で大きく検索しておいて、その後にアンダースコアで連結された文字など不要な部分を削除しています。 そういう処理をPerlのプログラミングで再現するには、皆さんの回答をきちんと理解して応用しなければなりません。(というか、欧州文字を含めて12文字以上を検索できる正規表現があれば一発なんですけどね) というわけで、お礼が遅れております。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

>どうやら12文字以上の単語が含む「行」を抽出しているようなのです そうですね。 while ( my $file = glob '*.txt' ) { open my $reading, '<', $file or die; my @content = <$reading>; close $reading; my @results = grep(/[a-zA-Z']{12,}/,@content); までを順に追っていきますが、 *.txtを一つ一つ開いて処理するのはいいですか? で、ファイルをオープンしたあとの my @content = <$reading>; でどうなるかというと、 content と言う配列に一行を一要素として収めます。 さて、 my @results = grep(/[a-zA-Z']{12,}/,@content); で何をしているかというと、@contentの要素、つまりテキストファイルの一行ごとに /[a-zA-Z']{12,}}/ とのマッチングを試みて、結果成功すれば(=12文字以上の単語があれば) その「行」を @results に集めるということです。 やり方はまあいろいろありますが、「単語同士は空白で分けられる」と言うことでよいのなら my @results = grep(/[a-zA-Z']{12,}/, split(' ', @content)); とすれば一応目的は達せられるでしょう。 単語の重複処理はまた別に。

Kazu_creator
質問者

補足

解説ありがとうございます。 >my @results = grep(/[a-zA-Z']{12,}/, split(' ', @content)); grep の検索対象の部分に split を入れ子にできるとは知りませんでした。 入門書レベルでは、そこまで書いてないので助かります。 しかし、実際に実行して見ると、LongWords_12.txtが出力されないようになってしまいました。これは、grepの検索条件に何もヒットしなかったためということでしょうか。 split(' ', @content)の区切り文字をsplit('\s', @content)としても同様です。

  • ORUKA1951
  • ベストアンサー率45% (5062/11036)
回答No.1

英文ですよね。 >どうやら12文字以上の単語が含む「行」を抽出しているようなのです  基本的に最大幅でマッチします。最小マッチが必要だが、そうすると  一行中最初に現れた文字列で終了する。  一行に空白文字で区切られた単語がある。ただし、途中で-を使った改行はない。(全行を一括して処理もできるが、メモリーとの関連で行頭に-があるときは戻るほうがよいかも)  この場合、一行を\sで区切って、それを一つ一つ調べる必要があります。 1) ファイルリスト(テキストファイル)をオープンして配列に読み込む 2) そのリストを順番に読んでファイルを開く 3) そのファイルを一行ずつ処理する。   3-1) 空白文字で区切って配列に入れる。   3-2) その配列を処理する。     マッチした単語を配列に入れていく     このときハッシュで重複をチェックする。   3-4) 次行へ 4) 次のファイルへ

Kazu_creator
質問者

補足

早速のご回答ありがとうございます。 >英文ですよね。 ドイツ語/フランス語ですが、欧州文字の扱いを考えると複雑になりそうなので、今は英語と仮定して書いています。 >2) そのリストを順番に読んでファイルを開く ここまでは、なんとか分かるのですが… >3) そのファイルを一行ずつ処理する。 このあたりから、苦しくなってきます。 どうやら、コンテキストのことがよくわかっていないようなので帰宅したら入門書にあたってみます。(処理した単語をハッシュに入れる方法などはサッパリです。恥ずかしながら。) とにかく、上記のアドヴァイスを参考にもう一度書いてみます。

関連するQ&A