Анализ положительных чисел из файла Часть-2


Часть-1 здесь

Это небольшой парсер CSV для положительных чисел основном предназначены для личных проектов.
Я (надеюсь) улучшен код от стороны-1 путем конденсации некоторых частях и совершенствование других, а также учета производительности то же самое.
Мои основные опасения каких-либо очевидных ошибок, я, возможно, пропустили, а также опасения по поводу общего подхода. Конечно, что-нибудь еще вы заметили, тоже добро пожаловать.
Пожалуйста, смотри часть-1 на список требований/ограничений у меня, когда пишу это.

Для тех, кто хочет проверить это на фактический ввод я обеспечиваю небольшой скрипт на Perl для генерации CSV-файлов.

Называют его: perl [scriptname] [how many lines you want] >outputfile

use strict;
use warnings "all";

die "usage: perl $0 <amount_of_lines>\n" unless (int(@ARGV) > 0);

for (1 .. $ARGV[0]) {
    my $vals_per_line = int(rand(10));
    my @values;
    for (0 .. $vals_per_line) {
        push(@values, int(rand(1000)));
    }
    print join(",", @values) . "\n";
}

Код:

#include <fstream>
#include <string>
#include <vector>

template<typename T>
void parse_lines(
        const std::string& filename,
        const uint_fast8_t& line_length_estimate,
        const uint_fast8_t& values_per_line_estimate,
        T callback) {
    std::ifstream infile{filename};
    if (!infile.good()) {
        return;
    }

    std::vector<uint_fast16_t> numbers;
    numbers.reserve(values_per_line_estimate);

    std::string buffer;
    buffer.reserve(line_length_estimate);

    while (infile.good() && std::getline(infile, buffer)) {
        if (buffer[buffer.size() - 1] == '\r') {
            buffer[buffer.size() - 1] = ',';
        }
        else {
            buffer.push_back(',');
        }

        uint_fast16_t parsed_number = 0;
        for (const auto& digit : buffer) {
            if (digit == ',') {
                numbers.emplace_back(parsed_number);
                parsed_number = 0;
            }
            else {
                parsed_number = (parsed_number * 10) + (digit - '0');
            }
        }
        callback(numbers);
        numbers.clear();
    }
}

int main(int argc, char** argv) {
    constexpr uint_fast8_t line_length_estimate = 50;
    constexpr uint_fast8_t values_per_line_estimate = 15;
    parse_lines(argv[1], line_length_estimate, values_per_line_estimate, [](auto& values) {
        // ...
    });
}


Комментарии
1 ответ

Здорово, что вы переименовали функцию по тому, что он делает. Я хотел бы предложить, что вы добавить вспомогательную функцию, которая читает одну строку.

Создание небольших хорошо инкапсулированы функции является ключевым в написании кода.

По поводу кода есть некоторые мелкие гниды:


  1. Шаблон код

    container[container.size() - 1] 

    могут всегда быть заменены

    container.back()

  2. По поводу парсинга можно использовать std::string::find_first_of а поиск следующего вхождения его.

    std::size_t first = 0;
    std::size_t second;
    while (first != buffer.size()) {
    second = buffer.find_first_of(",\r", first);
    // parse [first, second)
    first = second + 1;
    }

    Таким образом в интервале [первый, второй) всегда будут числа и вам не придется заменить последний символ или другие вещи


  3. Снова инкапсуляции. Нет никаких причин, почему этот блок кода не внутри своей собственной функцией:

    uint_fast16_t parsed_number = 0;
    for (const auto& digit : buffer) {
    if (digit == ',') {
    numbers.emplace_back(parsed_number);
    parsed_number = 0;
    }
    else {
    parsed_number = (parsed_number * 10) + (digit - '0');
    }
    }

    Даже лучше, если оформить его можно легко принять для итераторов/позиции

    uint_fast16_t parse_number(cont std::string& buffer, std::size_t first, std::size_t second) {
    uint_fast16_t parsed_number = 0;
    for (std::size_t i = first; i < second; ++i) {
    parsed_number = (parsed_number * 10) + (buffer[i] - '0');
    }
    return parsed_number;
    }

    Теперь вы можете написать цикл, как

    while (infile.good() && std::getline(infile, buffer)) {
    std::size_t first = 0;
    std::size_t second;
    while (first != buffer.size()) {
    second = buffer.find_first_of(",\r", first);
    numbers.push_back(parse_number(buffer, first,second));
    first = second + 1;
    }
    callback(numbers);
    numbers.clear();
    }

    Я бы даже предложил создать еще одну функцию для разбора одной строки:

    void parse_single_line(std::string& buffer, std::vector<uint_fast16_t>& numbers) {
    std::size_t first = 0;
    std::size_t second;
    while (first != buffer.size()) {
    second = buffer.find_first_of(",\r", first);
    numbers.push_back(parse_number(buffer, first,second));
    first = second + 1;
    }

    Вместе это приводит к

    while (infile.good() && std::getline(infile, buffer)) {
    parse_single_line(buffer, numbers);
    callback(numbers);
    numbers.clear();
    }

2
ответ дан 11 марта 2018 в 01:03 Источник Поделиться