java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

英単語の出現数をカウントする。

投稿日:

最終更新日:2016年08月15日

アイキャッチ

こんにちは。「Javaを復習する初心者」です。

今回は英文の中に出現する単語の出現数をカウントするということをやりました。

使う英文は以下の英文です。著作権フリーの文章を公開しているサイトに載っていたものの一つです。内容は分かりませんが使わせていただきました。

Rambles in Florida, Part 1 | American Naturalist: Rambles in Florida | R.E.C. Stearns | Lit2Go ETC

テキストファイルを用意

英文を直にプログラムの中に書くわけのは面倒なので今回はテキストファイルに英文を保存しておき、そのファイルを読み取るプログラミング方法を選択しました。英文はEclipseのプロジェクト直下にresourceフォルダを作り、RAMBLES_IN_FLORIDA_PART_1.txtというファイルを作って保存しました。プログラムの方でこのファイルを読み込み、単語の出現数をカウントします。

Mapを使う

カウントには以下のMap<String, Integer>を使いました。ジェネリックスに指定したクラスの組み合わせは以下の意味合いです。

キー
単語 出現数

単語wordが与えられたときの処理は以下の考え方です。

  1. 単語がMapのキーに含まれている場合、出現数を加算する。
  2. 単語がMapのキーに含まれていない場合、以下をMapに格納する。
    キー
    word 1

集計した単語を全て出力すると長いので上位10件を表示するようにしました。

プログラム

以下はソースと実行結果です。

ソース

package practice.test20160812;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Accumulate {

    private static final String FILE_PATH = "resource\\RAMBLES_IN_FLORIDA_PART_1.txt";

    private static final String SEPARATOR = "(\\s+?|\\.|,|;)";

    public static void main(String[] args) {

        // 集計
        Map<String, Integer> map = new HashMap<>();
        try (FileReader fr = new FileReader(FILE_PATH);
                BufferedReader br = new BufferedReader(fr)){
            String line;
            while ((line = br.readLine()) != null) {
                String[] words = line.split(SEPARATOR);
                for (String word : words) {
                    if (!word.isEmpty()) {
                        if (map.containsKey(word)) {
                            int count = map.get(word) + 1;
                            map.put(word, count);
                        } else {
                            map.put(word, 1);
                        }
                    }
                }

            }
        } catch (FileNotFoundException e) {
            System.out.println("ファイルが見つかりませんでした。");
        } catch (IOException e) {
            System.out.println("読み取りに失敗しました。");
        }

        // 出現数で降順に並べ替え、つづりの長さ最大値取得
        List<String> list = new ArrayList<>();
        int maxLengthOfSpelling = 0;
        for (String key : map.keySet()) {
            list.add(key);

            if (maxLengthOfSpelling < key.length()) {
                maxLengthOfSpelling = key.length();
            }
        }
        Collections.sort(list, (o1, o2) -> {
            return - map.get(o1) + map.get(o2);
        });

        // 上位10件出力
        System.out.println("出現回数トップ10");
        String format = "%-" + maxLengthOfSpelling + "s: %3d";
        for (String word : list) {
            int count = map.get(word);
            if (10 <= count) {
                System.out.printf(format, word, count);
                System.out.println();
            }
        }


    }

}

結果

出現回数トップ10
the              : 185
of               : 115
and              :  74
a                :  57
is               :  37
to               :  37
are              :  31
in               :  31
we               :  19
species          :  18
by               :  18
which            :  18
as               :  16
not              :  15
it               :  15
that             :  15
with             :  14
for              :  14
or               :  14
from             :  14
its              :  13
but              :  13
The              :  11
they             :  11
upon             :  10
be               :  10
on               :  10

プログラムのポイントをいくつか説明します。

集計のためにファイルを読み込むのですが、FileReaderクラスとBufferedReaderクラスのインスタンスは最後にclose()メソッドを呼び出すのが通常の方式のようです。リソースは、プログラムでの使用が終わったら閉じられなければいけないオブジェクトというのが理由です。今回はtry-with-resources文を使い、各リソースが確実に閉じられるようにします。これでclose()メソッド呼び出し忘れを防止することができます。

テキストファイルの読み取りの箇所ではreadLine()で行を取得してます。これをsplit()メソッドで分割しているのですが、今回使った分割の規則は「空白が1つ以上、ピリオド、カンマ、またはセミコロン」です。これは実際には出力結果をみて調節した内容です。ただ、分解後に空白1文字が残る場合があるらしく、単語の出現数のカウントではisEmpty()メソッドを使用してます。

出現数上位10件を表示するために、並べ替えの処理をしています。いったん、List<String>に単語を格納し、Collections.sort()メソッドでソートを実施しています。このとき、指定したComparatorではMapから出現数を取得して、その差を返却しています。今回は出現数の降順に並ぶように符号を調節しました。