Проверка орфографии приложение на C++


Это мое текущее курсовую работу для университета, где я должен написать проверку орфографии программы и скомпилировать с помощью C++11.

У меня есть словарь, который читается в программу автоматически и сохраняется в бинарное дерево поиска в порядке. Пользователь предоставляет текстовый файл, который они хотели бы проверить, который разбивается на лексемы и сохранить в строке вектора.

Затем программа просматривает строку векторный и проверяет, если это слово существует в БСТ. Если слово отсутствует в БСТ, он будет печатать слова в консоли, как неверно.

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <cstring>
#include "bst.h"

using namespace std;

string read_file(string &filename)
{
    ifstream file(filename);
    // If there is a file
    if (file)
    {
        string data;
        // Find total number of bytes of file
        file.seekg(0, file.end);
        data.resize(file.tellg());
        file.seekg(0, file.beg);
        // Read text into data string
        file.read(&data[0], data.size());
        // Close the file
        file.close();
        // Return the string
        return data;
    }
    else return string("");
}

void split_words(vector<string> &words, string &data)
{
    char *token, *delim = " .,?!:;/\"\'\n\t";
    // Set token to first word
    token = strtok(&data[0], delim);
    // Split rest of words
    while (token != nullptr)
    {
        // Convert each word from dictionary to lower case
        for (int i = 0; i < strlen(token); ++i)
        {
            char word = tolower(token[i]);
            token[i] = word;
        }
        // Push word to end of vector
        words.push_back(token);
        // Get the next word
        token = strtok(nullptr, delim);
    }
    // Free the memory
    token = nullptr;
    delim = nullptr;
    delete token;
    delete delim;
}

int main(int argc, char **argv)
{
    string file_to_check, file_data, word_dictionary = "dictionary.txt";
    int spell_count = 0;

    BinarySearchTree *tree = new BinarySearchTree();
    vector<string> words;

    // Loop through arguments
    for (int i = 0; i < argc; ++i)
    {
        // Set file name if provided as argument
        if (string(argv[i]) == "-i" && argv[i+1] != nullptr)
            file_to_check = argv[i+1];
    }

    // If there was no file name as argument, prompt user
    if (file_to_check.empty())
    {
        cout << "File name: ";
        getline(cin, file_to_check);
        cout << endl;
    }

    // If file name is not empty, run spell checking methods
    if (!file_to_check.empty())
    {
        // Read words from dictionary.txt into file_data string
        file_data = read_file(word_dictionary);
        // Split the words and store into vector
        split_words(words, file_data);
        // Insert words into Binary Search Tree
        for (int i = 0; i < words.size(); ++i)
            stringstream(words[i]) >> *tree;

        // Store the data read from specified file
        file_data = read_file(file_to_check);
        // Split sentences and store each word in words vector
        split_words(words, file_data);
        cout << endl;

        // Loop through words vector and check if it exists in dictionary
        for (int i = 0; i < words.size(); ++i)
        {
            // Print out non-occurring words
            if (!tree->exists(words[i]))
            {
                spell_count++;
                cout << words[i] << endl;
            }
        }
        cout << endl;

        // Print the total number of spelling mistakes
        cout << spell_count << " spelling mistakes" << endl;
    } 
    else
    {
        // If still no file specified, print message and exit
        cout << "No file specified!" << endl;
        return 0;
    }

    // Free the memory
    delete tree;

    return 0;
}


485
6
задан 1 апреля 2018 в 04:04 Источник Поделиться
Комментарии
4 ответа


  • using namespace std; никогда не делайте этого

  • Предпочитают использовать \n за std::endl а последний сбрасывает буфер, который редко и может негативно сказаться на производительности.

  • const ваши аргументы, когда это возможно. Подробнее о константной корректности

  • * и & считаются частью типа в C++, поэтому предпочитаю писать int& foo вместо int &foo. Также для *.

  • Никогда не опускать необязательные скобки, так как это приведет в конечном итоге к ошибкам.

  • Дойдя до конца главной автоматически возвращает 0, так что вы можете удалить трейлинг return 0

  • Не инициализировать несколько переменных в строке.

  • Мне нравится, что вы используете оператор префикс. Ваших имен тоже неплох, так зачем ты это делаешь?

    // Close the file
    file.close();

    или это

    // Loop through arguments
    for (int i = 0; i < argc; ++i)

    Ваши комментарии совершенно излишни и могут быть удалены.


  • Что это?

    // Free the memory
    token = nullptr;
    delim = nullptr;
    delete token;
    delete delim;

delete используется только после согласования new. Он чувствует, как вы чего-то недопонимаете в отношении управления памятью. Читать на этот раз.
новый
удалить
управление памятью


  • BinarySearchTree *tree = new BinarySearchTree();

Я не знаю, как это реализуется, но такая линия выглядит как-то от Java. Вам действительно нужно , чтобы положить это в куче?


  • Лично я думаю, что разбиение может быть сделано достаточно элегантно в регулярное выражение. Однако многие люди совершенно противоположный с помощью regex. Я также читал, что в прошлом производительность регулярных выражений в C++ был довольно беден, но может быть стоит измерения, чтобы увидеть, если это в допустимых пределах для вашего использования.

Пример регулярного выражения:

std::regex rx{R"(([^ .,?!:;/\"\'\n\t]+))"};
auto data_begin = std::sregex_iterator(data.begin(), data.end(), rx);
auto data_end = std::sregex_iterator();
for (std::sregex_iterator it = data_begin; it != data_end; ++it) {
// do something with words here
}

5
ответ дан 1 апреля 2018 в 06:04 Источник Поделиться

У меня есть еще два замечания, чтобы добавить к ответу Юрия:


  1. Предпочитаю всегда инициализировать переменные в момент, когда вы объявите им:

    char *token, *delim = " .,?!:;/\"\'\n\t";
    token = strtok(&data[0], delim);

    должно быть

    char const* delim = " .,?!:;/\"\'\n\t";
    char* token = strtok(&data[0], delim);

  2. И не объявлять переменные, пока вы в них нуждаетесь. В C вы должны объявлять все переменные в начале функции (я думаю, что новый стандарт не требует больше никаких?) но в C++ нет. Есть преимущества того, чтобы объявить переменные там, где они вам нужны: вы можете инициализировать их в значимое значение, и вы уменьшите их объем, снижая вероятность ошибки.

    Например spell_count может быть объявлен непосредственно перед началом цикла, который использует его, внутри if заявление. Точно так же для всех строк, за исключением имени файла.


О, еще одна вещь. Три комментария. Три замечания, чтобы добавить к ответу Юрия:


  1. Я хотел бы предложить, используя constexpr для констант, или, по крайней мере const:

    constexpr char const* word_dictionary = "dictionary.txt";

    Также обратите внимание на const я добавил к delim переменной в соответствии с пунктом 1. Я думаю, вы должны получить как минимум предупреждение компилятора, если вы не, потому что ты показываешь на данные unmutable.

    Я предпочитаю константы должны быть объявлены в верхней части файла, где они легко обнаружить. По крайней мере, константы, как word_dictionary что можно настраивать в исходном коде, и вы, возможно, захотите настроить в какой-то момент.


4
ответ дан 3 апреля 2018 в 02:04 Источник Поделиться

Все важное уже было сказано. Просто несколько незначительных заметок:


  1. вы должны сократить вложения в read_file функция.

    Вместо:

    if (file)
    {
    // Load the file
    return data;
    }
    else return string("");

    Вы можете сделать:

    if (!file)
    {
    return string("");
    }

    // Load the file
    return data;

    Это делает код намного более читабельным.


  2. Если загрузка файла не удается, вы просто возвращать пустую строку, а затем попытаться разобрать его. Это молчание не является очень опасным, вы должны определенно рассмотреть возможность бросать исключение, если файл открыть не удается:

    if (!file)
    {
    throw std::runtime_error("Dictionary failed to load");
    }

    Это изменение, очевидно, требует try - catch блок внутри функции main.


3
ответ дан 4 апреля 2018 в 01:04 Источник Поделиться

Ошибки выводятся в стандартный поток ошибок, и привести к завершению программы с ошибкой (не нулевое) состояние:

    std::cerr << "No file specified!\n";
return 1;

1
ответ дан 4 апреля 2018 в 01:04 Источник Поделиться