Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Распозноваие слов


Автор: Karta 14.5.2012, 13:22
Добрый день.
Начала изучать джаву, что бы потренироваться , пытаюсь выполнить задание.

Пытаюсь написать программу, которая будет принимать в качестве аргумента имя текстового файла, и выводить CSV файл с колонками.
1. Слово.
2. Частота.
3. Частота (в %).
Разделителями считать все символы кроме букв и цифр.

Вот у меня вопрос. В какой последовательности надо реализовать поставленную задачу. Мне кажется, что сначала нужно с помощью InputStreamReader прочесть файл, затем наверное использовать StringBuilder для построения слов. А как строить и распозновать слова в прочитанном тексте? Нужно создавать массивы букв, цыфр, знаков препинания и потом сравнивать каждый символ из текста с символом из массива и запоминать?

Как последовательнос построить решения задачи?

Заранее спасибо 

Автор: AntonSaburov 14.5.2012, 13:48
Для начала можно просто прочесть файл по строкам (их проще разбирать). Считаем, что файл не большой и все уникальные слова из него могут поместиться в памяти.
Пример ниже. И в самом конце комментарий про разбивку - там разбить считанную строку и наверно самое простое класть слова в Map. Вернее сначала ищем слово в Map где ключом является слово, а величиной является счетчик. По поводу параметра "частота" не совсем понятно как он вычисляется - видимо это количество этого слова во всем тексте. Значит сначала считаем все слова, а потом обработка - суммируем все счетчики и считаем процент.

Код

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

/**
 * Класс для демонстрации построчного чтения из текстового файла
 */
public class Main {

    private static final String TEST_FILE = "testFile.txt";

    public static void main(String[] args) {
        Main m = new Main();
        // Вызываем основную процедуру примера, которая может сгенерить исключения
        try {
            m.readFile();
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    // Основная процедура демонстрации чтения строк из файла
    private void readFile() throws FileNotFoundException, IOException {
        // Открываем символьное чтение из файла созданием объекта стандартного FileReader
        // "Скрепляем его с другим считывателем, который
        BufferedReader br = new BufferedReader(new FileReader(TEST_FILE));

        String s = null;
        // Пока считанная строка не будет равна null - значит файл еще не кончился
        // и продолжаем чиать строку за строкой с помощью метода readLine
        while ((s = br.readLine()) != null) {
            // Выводим на экран считанную строку
            System.out.println("Read:" + s);

            // И вот тут мы можем разобрать нашу строку регулярным выражением через split на слова и обработать


        }
    }
}


Автор: Karta 14.5.2012, 14:05
Ой то, что надо, спасибо огромное  smile 

Автор: Pawl 14.5.2012, 21:04
Задачка стандартная. Ее сейчас много где задают для проверки знаний io и collection. В этом случае лучше использовать Scanner. Посмотрите мой пост в http://forum.vingrad.ru/topic-346814.html теме. Там, правда, я делал вывод в обычный текстовый файл и не подсчитывал частоты встречаемости, но идея та же, и, прошу заметить, использовал для решения всего 2 цикла! smile 

Автор: Karta 15.5.2012, 23:28
Я в вашей работе практически ничего не поняла...
Спасибо, но я лучше длинно по простому, как сама додумаюсь.

Автор: Pawl 17.5.2012, 11:28
Цитата(Karta @  15.5.2012,  23:28 Найти цитируемый пост)
Спасибо, но я лучше длинно по простому, как сама додумаюсь.

Типа, лучшее враг хорошего? smile 
Я тут переделал свою прогу под Ваши нужды, правда, пришлось добавить еще 1 цикл для расчета в %. Напишу коменты и выложу в конце дня. ИМХО, лучше писать сразу хорошо, чем "по простому", хотя в первом случае и разбираться надо больше.

Автор: Pawl 17.5.2012, 17:03
Вот, как и обещал, выкладываю для Вас программку. Для ее реботы требуется библиотека guava-12.0. Скачать ее Вы сможете http://code.google.com/p/guava-libraries/. Программа состоит из 2-х классов и снабжена подробными комментариями. Если что, пишите smile .
Код

package sorting;

//первые 2 импорта - из сторонней библиотеки guava-12.0. Она расширяет возможности пакета java.util
//Multimap - мультикарта, позволяющая хранить ключ и несколько значений к нему, ArrayListMultimap - ее реализация
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Scanner;

//класс для чтения из текстового файла слов
public class Maker {
    /**
     * метод, кототый читает из текстового файла слова, подсчитывает их количество и частоту встречаемости в %
     * @param fileName - имя входного текстового файла
     * @param delimiter - разделитель слов
     * @return мультикарта, содержещая слова, их количество и частоту встречаемости в %
     * @throws IOException может возникнуть, если файл для чтения не найден или в процессе чтения произошел сбой
     */
    public static Multimap<String, Float> getData(String fileName, String delimiter) throws IOException {
        //общее количество слов во входном файле
     int sum = 0;
        //карта для хранения слов (ключи) и их количеств (значения)
     HashMap<String, Float> list = new HashMap<>();
        /*
         * создаем мультикарту. Ее ключами будут слова из файла с именем fileName, значениями - 
         * их количества и частоты встречаемости в %. Тип значений Float (с плавающей точкой)
         * взят для того, чтобы частоты встречаемости рассчитывались более точно, а не округленно до целого
         */
        Multimap<String, Float> wqf = ArrayListMultimap.create();
        //читаем из файла слова, разделенные разделителями, указанными в параметре delimiter
        Scanner scan = new Scanner(new FileReader(fileName)).useDelimiter(delimiter);
        //читаем, пока не кончатся слова
        while (scan.hasNext()) {
            //заносим в карту слова
            String newWort = scan.next();            
            if (!list.containsKey(newWort)) {
                //если данное слово еще не встречалось в карте, его количество = 1
                list.put(newWort, 1f);
            } else {
                //если уже встречалось - количество увеличиваем на 1
                list.put(newWort, list.get(newWort) + 1);
            }
            //увеличиваем на 1 общее количество слов
            sum++;
        }
        //рассчитываем для каждого слова частоты его встречаемости в %
        for (Entry<String, Float> wq : list.entrySet()) {
            //расчет частоты
            Float frequency = wq.getValue() * 100 / sum;
            //заносим в мультикарту данное слово и его количество в файле
            wqf.put(wq.getKey(), wq.getValue());
            //заносим в мультикарту данное слово и частоту его встречаемости 
            wqf.put(wq.getKey(), frequency);
        }
        //закрываем сканер
        scan.close();
        //возвращаем мультикарту
        return wqf;
    }
}

Код

package sorting;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

//класс для записи в файл csv
public class WirterCSV {
    //разделители слов во входном файле - не буквы и не цифры
    private static final String DELIMITER = "[^\\p{IsAlphabetic}|^\\p{Digit}]+";
    //имя выходного csv-файла
    private static final String OUT_FILE = "out.csv";
    
    public static void main(String ...args) {
        //открываем для записи файл csv
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(OUT_FILE))) {
            //получаем мультикарту и преобразовываем ее в обычную карту, где ключами являются слова,
            //а значениями - коллекции, содержащие количество и частоту данного слова
            Map<String, Collection<Float>> myСollection = Maker.getData(args[0], DELIMITER).asMap();
            //объект StringBuilder, в который по очереди будем вносить слово, его количество и частоту
            StringBuilder result = new StringBuilder();
            //"шапка" таблицы в csv-файле.
            writer.write("слово;к-во;частота,%");
            writer.newLine();
            //добавляем в result слово, его количество и частоту
            for (Entry<String, Collection<Float>> wqf : myСollection.entrySet()) {
                result.append(wqf.getKey()).append(";");
                for (Float value : wqf.getValue()) {
                    result.append(value).append(";");
                }
                //преобразуем result в строку и заменяем в ней точки на запятые для корректного вывода дробных чисел
                String s = result.toString().replace('.', ',');
                //пишем в строчу csv-файла слово, его количество и частоту в отдельные ячейки
                writer.write(s);
                //переходим на новую строку
                writer.newLine();
                //очищаем объект StringBuilder для повторной записи
                result.delete(0, result.length());
            }
            //после того, как все записано в файл, закрываем файл.
            writer.close();
        //IOException может возникнуть, если в процессе записи произошел сбой или в результате
        //неправильной работы метода getData класса Maker
        } catch(IOException e) {
            //соответствующее сообщение об ошибке выводим на консоль
            System.err.println(e.getMessage());
        //возникнет, если в коммандной строке не задано имя файла
        } catch (ArrayIndexOutOfBoundsException a) {            
            System.err.println("put existing file name into command line");
        }
    }
}

Автор: Karta 23.5.2012, 16:28
 smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)