ハリー・ポッター英語版テキストの文字と単語の出現頻度分析

要件

  1. ハリー・ポッター英語版テキストファイルを読み込み、各英字の出現確率を計算し(大文字小文字を区別)、降順で表示する

  2. テキストファイル内の各単語の出現回数をカウントし、頻度の高い単語を指定された形式で出力する

機能1 ファイル内のすべてのユニークな単語を、出現回数の多い順に並べ替え、出現回数が同じ場合は辞書順に並べ替えて出力する

機能2 指定されたディレクトリ内の各ファイルに対して機能1を実行する

機能3 指定されたディレクトリとそのサブディレクトリ内のすべてのファイルに対して機能1を実行する

問題点

  1. 単語の識別方法:非英字記号で単語を分割するために正規表現を使用する
// 非英字で単語を分割
String[] words = text.split("[^a-zA-Z]+");
  1. 単語と関連データの保存方法:Mapを使用し、キーをString型(単語)、値を適切な型に設定する
// 出現回数を記録するハッシュマップ
Map<String, Integer> frequencyMap = new HashMap<>();

主要な考え方

ファイルデータを読み込み、char型に変換し、大文字小文字を区別して文字の出現回数をカウントする。同時に、文字の総数もカウントする。

  1. BufferedReaderを使用してバッファリングされたストリームで読み込み、データをStringBufferに追加する。StringBuffer内のデータをchar型に変換し、ループ処理を行う。Character.isLetter()メソッドで文字かどうかを判断し、Character.isLowerCase()で小文字かどうかを判断する。配列を使用して各文字の出現回数を保存する。小文字の場合、配列のインデックスはその文字('a'-'a')(ASCIIコード表を使用)、0-25のインデックスは小文字を保存し、0-26のインデックスは大文字を保存する。大文字のインデックスの表現方法:大文字の場合は'a'-'A'+26。

新しいdouble配列を長さ52で作成し、各単語の出現頻度パーセンテージを順番に保存する。Map配列を作成し、キーに英字、値に頻度パーセンテージを保存する。Listコレクションを定義し、Map型を格納する。Collections.sort()メソッドを呼び出して並べ替え、Listコレクションをループ処理してキーと値を出力する。

  1. 入力された単語をまずString配列に保存し、String配列をループ処理する。該当する単語が保存されていない場合はMapを作成し、既に存在する場合はvalueを+1する。同時にnumberを単語の総数に設定する。

ループ処理後、Map配列のvalue値を並べ替え、最終的に頻度の高い単語とその出現確率を指定された形式で出力する。

機能1 機能2のデータを呼び出し、カスタムハッシュテーブルの並べ替えメソッドを実装して、機能要件に従って並べ替える

機能2 FileのisFileメソッドでパスがファイルかどうかを判断し、ファイルの場合は機能1を実行し、そうでない場合はディレクトリ内のファイルをFile型配列に保存し、ループ処理して機能1を実行する。

機能3 機能2と同様に、パスがディレクトリの場合はディレクトリをループ処理し、ディレクトリの場合はさらにループ処理を続け、そうでない場合は機能1を実行する。

ソースコード

package text.analysis;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class TextAnalyzer {
    
    // 単語を検索し、指定された形式で出力する
    public static void analyzeWords(String filePath) throws Exception {
        
        File file = new File(filePath);
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        StringBuffer stringBuffer = new StringBuffer();
        String line = null;
        
        while((line = bufferedReader.readLine()) != null) {
            stringBuffer.append(line);
        }
        bufferedReader.close(); 
        
        String text = stringBuffer.toString().toLowerCase();
        String[] words = text.split("[^a-zA-Z]+");
        
        Map<String, Integer> frequencyMap = new HashMap<>();
        double totalWords = 0;
        double uniqueWords = 0;
        
        for(String word : words){  
            if(frequencyMap.get(word) == null){  
                frequencyMap.put(word, 1);
                totalWords++;
                uniqueWords++;
            } else {
                frequencyMap.put(word, frequencyMap.get(word) + 1);  
                totalWords++;
            }
        }
        
        List<Map.Entry<String, Integer>> wordList = new ArrayList<>(frequencyMap.entrySet()); 
        
        Comparator<Map.Entry<String, Integer>> comparator = new Comparator<Map.Entry<String, Integer>>() {
            public int compare(Map.Entry<String, Integer> left, Map.Entry<String, Integer> right) 
            {
                return left.getValue().compareTo(right.getValue());
            }
        };
        
        Collections.sort(wordList, comparator);
        
        System.out.println("単語の総数:" + totalWords);
        System.out.println("ユニークな単語の数:" + uniqueWords);
        System.out.println("表示する上位N個を入力してください:");
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        
        for(int i = 0; i < n; i++) {
            double percentage = (wordList.get(wordList.size() - i - 1).getValue() / totalWords) * 100;
            System.out.println(wordList.get(wordList.size() - i - 1).getKey() + ":" + 
                             wordList.get(wordList.size() - i - 1).getValue() + " " + 
                             String.format("%.2f", percentage) + "%");
        }
        
        Hashtable<String, Integer> wordTable = new Hashtable<>();
        
        for(String word : words){  
            if(wordTable.get(word) == null){  
                wordTable.put(word, 1);
            } else {
                wordTable.put(word, wordTable.get(word) + 1);  
            }
        }
        
        sortWordTable(wordTable);
    }
    
    // カスタムハッシュテーブルの並べ替え
    private static void sortWordTable(Hashtable<String, Integer> wordTable) {
        List<Map.Entry<String, Integer>> list = new ArrayList<>(wordTable.entrySet());
        
        list.sort(new Comparator<Map.Entry<String, Integer>>() 
        {
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2)
            { 
                int alphabeticalComparison = o1.getKey().compareTo(o2.getKey());
                if(o1.getValue() < o2.getValue())                    
                {
                    return 1;
                }
                else if(o1.getValue() == o2.getValue())    
                {
                    if(alphabeticalComparison > 0)                                   
                    {
                        return 1;
                    }
                }
                return -1;
            }
        });
        
        System.out.println("機能1:出現回数の多い順にユニークな単語を表示し、出現回数が同じ場合は辞書順に並べ替える");
        for (Map.Entry<String, Integer> entry : list) 
        {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
    
    // 文字を検索し、指定された形式で出力する
    public void analyzeCharacters(String filePath) throws Exception {
        FileReader fileReader = new FileReader(filePath);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        StringBuffer stringBuffer = new StringBuffer();
        String line = null;
        
        while((line = bufferedReader.readLine()) != null) {
            stringBuffer.append(line);
        }
        bufferedReader.close(); 
        
        char[] characters = stringBuffer.toString().toCharArray();
        
        double totalLetters = 0;
        int[] letterCount = new int[52];
        
        for(int i = 0; i < characters.length; i++) {
            if(Character.isLetter(characters[i])) {
                if(Character.isLowerCase(characters[i])) 
                {
                    letterCount[characters[i] - 'a']++;
                    totalLetters++;
                } else {
                    letterCount[characters[i] - 'A' + 26]++;
                    totalLetters++;
                }
            }
        }
        
        double[] frequencies = new double[52]; 
        for(int i = 0; i < 52; i++) {
            frequencies[i] = letterCount[i] / totalLetters * 100;
        }
        
        Map<String, Double> frequencyMap = new HashMap<>();
        
        for(int i = 0; i < frequencies.length; i++) {
            if(i < 26) {
                char lowercase = (char)(i + 97);
                frequencyMap.put(String.valueOf(lowercase), frequencies[i]);
            } else {
                char uppercase = (char)(i + 65 - 26);
                frequencyMap.put(String.valueOf(uppercase), frequencies[i]);
            }
        }
        
        List<Map.Entry<String, Double>> letterList = new ArrayList<>(frequencyMap.entrySet()); 
        
        Comparator<Map.Entry<String, Double>> comparator = new Comparator<Map.Entry<String, Double>>() {
            public int compare(Map.Entry<String, Double> left, Map.Entry<String, Double> right) 
            {
                return left.getValue().compareTo(right.getValue());
            }
        };
        
        Collections.sort(letterList, comparator); 
        
        for(int i = 0; i < frequencies.length; i++) {
            System.out.println(letterList.get(letterList.size() - i - 1).getKey() + ":" + 
                             String.format("%.2f", letterList.get(letterList.size() - i - 1).getValue()) + "%");
        }
    }
    
    // 再帰的にディレクトリ内のすべてのサブディレクトリを走査する
    @SuppressWarnings("null")
    static void traverseDirectory(String path) throws Exception {
        TextAnalyzer analyzer = null; 
        File directory = new File(path);
        
        File[] files = directory.listFiles();
        for(int i = 0; i < files.length; i++) {
            if(files[i].isDirectory()) {
                traverseDirectory(files[i].getPath());
            } else {
                analyzeWords(files[i].getPath());     
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        TextAnalyzer analyzer = new TextAnalyzer();
        String harryPotterFile = "C://Users//Desktop//Harry Potter and the Sorcerer's Stone.txt";
        String directoryPath = "D://text//analysis";
        
        System.out.println("英語テキストファイル内の26文字の出現頻度を、高い順に並べ替え、パーセンテージを小数点以下2桁まで表示します:");
        analyzer.analyzeCharacters(harryPotterFile);
        
        File file = new File(directoryPath);
        if(file.isFile()) {
            System.out.println("機能1:出現回数の多い順にユニークな単語を表示し、出現回数が同じ場合は辞書順に並べ替える");
            analyzer.analyzeWords(directoryPath);
        } else {
            traverseDirectory(directoryPath);
        }
    }
}

タグ: Java テキスト処理 ファイル操作 文字頻度分析 単語カウント

5月16日 03:44 投稿