Парсин текстового файла
От: svsanek  
Дата: 29.08.08 12:28
Оценка:
Доброе всем время суток. Есть задача:
Исходные данные:
Входной поток — набор слов. Для простоты предполагается, что данные хранятся в текстовом файле, но при этом общее количество слов и размер файла заранее неизвестны, т.е. нельзя прочитать в память весь файл целиком. Кодировка — Задается.
Необходимо выполнить разбора входного текста на слова и вычислить частоту встречаемости каждого слова. В качестве "слова" понимается набор символов, состоящий из букв, цифр и символа подчеркивания.
Все остальные символы считаются разделителями.
При проектировании следует исходить из предположения, что создаваемый код будет использоваться в рамках реальной системы.
Требования: расширяемость, производительность, экономия памяти
Результат:
Вывести в текстовый файл список слов со значениями частоты встречаемости.
Должна быть предусмотрена возможность вывода списка, отсортированного по частоте.
Сортировка по словам не требуется.
Язык реализации: Java.
Есть вот такая реализация этой задачи:
Основной класс
package parser;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
/**
 * 
 * @author Alexander
 * Main Class
 *
 */
public class Parser {

    private static String inputFilePath;

    private static String outputFilePath;

    private static String encoding;

    /**
     * @param args
     */
    public static void main(String[] args) {
        InputStream is = Object.class.getResourceAsStream("/regexp.properties");
        Properties props = new Properties();
        try {
            //load properties
            props.load(is);
            String regexpString = props.getProperty("regexp");
            TextParser.setRegexp(regexpString);
            String maxBuffSize = props.getProperty("maxBufferSize");
            TextParser.setMAX_BUFF_SIZE(Integer.parseInt(maxBuffSize));
            inputFilePath = props.getProperty("inputFilePath");
            outputFilePath = props.getProperty("outPutFilePath");
            encoding = props.getProperty("encoding");
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("no properties");
            System.exit(666);
        }
        BufferedReader breader;
        try {
            System.out.println("start work");
            //open file with name=inputFileName and with encoding=encoding
            breader = new BufferedReader(new InputStreamReader(
                    new FileInputStream(inputFilePath), encoding));
            TextParser.textParsing(breader);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(outputFilePath), encoding));
            ParserUtil.GenerateReport(writer);
            System.out.println("complete work");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }

    }

}

Он собственно загружает проперти и открывает потоки.
Есть класс утиль, в котором содержатся утилитарные методы

package parser;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;


/**
 * 
 * @author Alexander
 * Class with utilits methods
 */
public class ParserUtil {

    //Map for storing lexems: String - lexem, Integer - count
    static HashMap<String, Integer> countsOfWords = new HashMap<String, Integer>();

    //sortin map by value
    private static List SortMap(HashMap<String, Integer> map) {
        List entryList = new ArrayList(map.entrySet());
        Collections.sort(entryList, new ValuesComparator());
        return entryList;
    }

    //method for adding lexem to the map
    public static void AddWordToHashMap(String newWord) {
    
        if (countsOfWords.containsKey(newWord)) {
            Integer count = countsOfWords.get(newWord);
            count++;
            countsOfWords.put(newWord, count);
        } else {
            Integer count = 1;
            countsOfWords.put(newWord, count);
        }
    }

    //write result into the file
    public static void GenerateReport(BufferedWriter writer) throws IOException {
        List entryList = ParserUtil.SortMap(countsOfWords);
        Entry<String, Integer> entry;
        for (Iterator itr = entryList.iterator(); itr.hasNext();) {
            entry = (Entry<String, Integer>) itr.next();
            writer.write(entry.getKey() + "=" + entry.getValue() + " ");
        }
        writer.close();

    }

}

Класс в котором производится парсинг текста.

package parser;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 
 * @author Alexander
 * Class for text parsing
 *
 */
public class TextParser {
    private static int MAX_BUFF_SIZE;

    private static String regexp;

    private static boolean partFlag;

    private static String completeString;

    /**
     * This method load data from txt file into the buffer and use
     * java.util.regex.Matcher, and java.util.regex.Pattern for finding lexems
     * 
     * @param breader -
     *            buffered reader
     * @throws IOException
     */
    public static void textParsing(BufferedReader breader) throws IOException {
        // compile regular expresion
        Pattern lexem = Pattern.compile(regexp);
        int bufferCount;
        // word - founded lexem
        String word = new String();
        // part of lexem
        String tmpString = new String();
        char[] charBuffer = new char[MAX_BUFF_SIZE];
        partFlag = false;
        // reads chars from file into the buffer
        // bufferCount - count of chars wich we had read
        while ((bufferCount = breader.read(charBuffer)) != -1) {
            // create new string
            String str = new String(charBuffer, 0, bufferCount);
            // use matcher for finding lexems
            Matcher mat = lexem.matcher(str);
            while (mat.find()) {
                // find lexem
                word = mat.group();
                // if lexem < then our buffer and there is no lexem's part in
                // buffer
                if ((mat.end() < MAX_BUFF_SIZE) && !partFlag) {
                    // we founded lexem
                    completeString = mat.group();
                } else {
                    // find secod part of the lexem
                    if ((mat.start() == 0) && partFlag
                            && (mat.end() != MAX_BUFF_SIZE)) {
                        completeString = tmpString + mat.group();
                        partFlag = false;
                    } else {
                        // if second part > then our buffer
                        if ((mat.start() == 0) && partFlag
                                && (mat.end() == MAX_BUFF_SIZE)) {
                            String secondPart = new String(tmpString);
                            tmpString = secondPart + mat.group();
                            partFlag = true;
                        } else {
                            // if lexem > then our buffer
                            if ((mat.end() == MAX_BUFF_SIZE)) {
                                // set flag
                                partFlag = true;
                                // store part of the string
                                tmpString = word;
                            } else {
                                completeString = tmpString;
                                partFlag = false;
                            }
                        }

                    }
                }
                if (!partFlag) {
                    ParserUtil.AddWordToHashMap(completeString);
                }
            }
        }
        breader.close();
    }

    public static void setMAX_BUFF_SIZE(int max_buff_size) {
        MAX_BUFF_SIZE = max_buff_size;
    }

    public static void setRegexp(String regexp) {
        TextParser.regexp = regexp;
    }
}

И последний класс — который позволяет сортировать мэп по валуе

package parser;

import java.util.Comparator;
import java.util.*;

/**
 * 
 * @author Alexander
 * Class for compare values from hash map
 *
 */

public class ValuesComparator implements Comparator {

    public int compare(Object arg0, Object arg1) {
        Map.Entry first = (Map.Entry)arg0;
        Map.Entry second = (Map.Entry)arg1;
        Comparable comparableFirst = (Comparable)first.getValue();
        Comparable comparableSecond = (Comparable)second.getValue();        
        return comparableFirst.compareTo(comparableSecond);
    }

}

И наконец — файл с пропертис

regexp=[\\w+[\\p{L}+[\\d+]]]+
inputFilePath=D:\\Eclipse\\Test\\1.txt
outPutFilePath=rezult.txt
maxBufferSize=10
encoding=KOI8-R


Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?
Re: Парсин текстового файла
От: Blazkowicz Россия  
Дата: 29.08.08 12:53
Оценка:
Здравствуйте, svsanek, Вы писали:

S>Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?


Откажитесь от регулярных выражений. Откройте для себя профайлер. "производительность" и "экономия памяти" взаимоисключающие критерии. Добавьте многопоточную обработку данных.
Re: Парсин текстового файла
От: Nicht Россия  
Дата: 29.08.08 12:59
Оценка:
Здравствуйте, svsanek, Вы писали:

S>Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?


Первый вопрос по алгоритму.
Ты читаешь файл в буффер потом на его основе делаешь строку и там ищешь слова. Что будет если конец одного буффера и начало следующего окажется на середине слова? Мне кажется в таком случае у тебя одно слово будет трактоваться как два. Может быть я не прав, код достаточно запутанный.

Второе — писать так программы на java — это плохой тон. Java — это обектно ориентированный язык, а у тебя мало того что все делается из статических метододв так еще и данные все сохранятся и передаются из объекта в объект через статические переменные. Свидетельствует о том что программу то написать ты можешь но java не знаешь. Так что, если хочешь произвести впечатление на потенциальных работодателей, советую код переписать.
Re: Парсин текстового файла
От: pagrus  
Дата: 29.08.08 13:22
Оценка:
S>Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?

Если допустимо использование java5+, примените java.util.Scanner. Получите гораздо более простой код, и "экономное" использование памяти.

Маленький коментарий: требования у вас не ахти, абсолютно не раскрыты и не формализованы. Это как написать "интерфейс приложения должен быть удобным".
Если бы например вместо слова "производительность" было сказано "программа должна обрабатывать файл в 10 тыс. слов не более чем за 10 секунд", было бы значительно лучше.
Так им и передайте =)

(ещё меня терзают смутные сомнения — не помогаем ли мы человеку "схалтурить" при трудоустройстве).
Re[2]: Парсин текстового файла
От: Blazkowicz Россия  
Дата: 29.08.08 13:25
Оценка:
Здравствуйте, pagrus, Вы писали:

P>(ещё меня терзают смутные сомнения — не помогаем ли мы человеку "схалтурить" при трудоустройстве).

Нам не все ли равно? Мы работодателям доступ к сайту не ограничиваем. Кому надо — прочитают.
Re[2]: Парсин текстового файла
От: Аноним  
Дата: 29.08.08 14:35
Оценка:
Здравствуйте, pagrus, Вы писали:

S>>Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?


P>Если допустимо использование java5+, примените java.util.Scanner. Получите гораздо более простой код, и "экономное" использование памяти.


P>Маленький коментарий: требования у вас не ахти, абсолютно не раскрыты и не формализованы. Это как написать "интерфейс приложения должен быть удобным".

P>Если бы например вместо слова "производительность" было сказано "программа должна обрабатывать файл в 10 тыс. слов не более чем за 10 секунд", было бы значительно лучше.
P>Так им и передайте =)

P>(ещё меня терзают смутные сомнения — не помогаем ли мы человеку "схалтурить" при трудоустройстве).


Насчет требований — вы правы. Насчет халтуры с работой — глубоко ошибаетесь =) Про сканнер нужно поподробней почитать.
Re[3]: Парсин текстового файла
От: pagrus  
Дата: 29.08.08 17:20
Оценка:
А>Насчет требований — вы правы. Насчет халтуры с работой — глубоко ошибаетесь =) Про сканнер нужно поподробней почитать.

Не серчайте, никого не хотел обидеть. Уж больно задача похожа на тестовую.
Re: Парсин текстового файла
От: Eugeny__ Украина  
Дата: 03.09.08 12:58
Оценка:
Здравствуйте, svsanek, Вы писали:

S>Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?


Регулярные выражения(особенно с вложенными "+" и без якорей) крайне не рекомендуется использовать в узких местах. Надо писать все ручками.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Re: Парсин текстового файла
От: Аноним  
Дата: 03.09.08 14:57
Оценка:
В EPAM собрался? Welcome!

Добавь джавадоку, ее тут смотрят, вернее смотрят, что ты умеешь ею пользоваться.
Re[2]: Парсин текстового файла
От: pagrus  
Дата: 04.09.08 08:51
Оценка:
Здравствуйте, Аноним, Вы писали:

А>В EPAM собрался? Welcome!


А>Добавь джавадоку, ее тут смотрят, вернее смотрят, что ты умеешь ею пользоваться.

А>)

Таки это было тестовое задание?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.