Файл парсинга кода на C++


Я обрабатываю около 3,5 Гб/15минут, и я был интересно, если я мог бы сделать это до лучшего времени...

Обратите внимание, что это очень конкретный файл CSV, так что я не волнует общность - только скорость внедрения и оптимизации.

int main(int argc, char *argv[]) {
    std::string filename;
    std::cout << "Please type the file name: " << std::endl;
    std::cin >> filename;

    std::string ticker;
    std::cout << "Please enter the ticker: " << std::endl;
    std::cin >> ticker;

    std::ifstream instream(filename.c_str());
    std::string ask_filename = ticker + "_ASK.NIT";
    std::ofstream askstream(ask_filename.c_str());

    std::string bid_filename = ticker + "_BID.NIT";

    std::ofstream bidstream(bid_filename.c_str());

    std::string line;
    std::getline(instream,line);

    while(std::getline(instream,line))
    {
        std::stringstream lineStream(line);
        std::string cell;
        std::string new_line;
        std::vector<std::string> my_str_vec;
        while(std::getline(lineStream,cell,','))
        {
            my_str_vec.push_back(cell);
            //new_line.append(cell.append(";"));
        }
        // works on date
        std::string my_date = my_str_vec[0];
        std::string::iterator my_iter;
        std::string processed_date = "";
        for(my_iter = my_date.begin(); my_iter != my_date.end(); ++my_iter) {
            if(std::isalnum(*my_iter) || *my_iter == ' ')
                processed_date.append(1,(*my_iter));
        }

        my_str_vec[0] = processed_date;


        std::vector<std::string>::iterator my_vec_iter;
        for(my_vec_iter = my_str_vec.begin() + 1; my_vec_iter != my_str_vec.end(); ++my_vec_iter) {
            std::string my_semicol = ";";
            *my_vec_iter = my_semicol.append(*my_vec_iter);
        }

        askstream << my_str_vec[0] << my_str_vec[1] << my_str_vec[3] << std::endl;
        bidstream << my_str_vec[0] << my_str_vec[2] << my_str_vec[4] << std::endl;
    }

    askstream.close();
    bidstream.close();


    return 0;
}


2767
4
задан 4 сентября 2011 в 05:09 Источник Поделиться
Комментарии
4 ответа

Во-первых ручьев и конкретно поток операторы >> и << предназначены для надежность а не скорость. Они делают код менее неправильно пишут (и предоставить некоторые местные конкретных преобразований), которые делают дорого с точки зрения использования (считай меняется на C функции fscanf (), если скорость имеет первостепенное значение).

Так что если скорость у вас цель потоков-не лучший выбор.

Предполагая, что мы остаемся с потоками (задать другой вопрос о том, как написать это в C для скорости):

Мертвый Код

Второе удаление мертвого кода (это смущает) и затрат на создание.

    std::string new_line;

Небольшие Недостатки

Нет необходимости создавать ячейку объект каждый раз через петлю создать ее один раз вне цикла. (Лично я бы оставил его в петли, но если вы собираетесь на скорости, то это явная простота спасет).

При сканировании дату вы делаете копию значения:

    std::string my_date = my_str_vec[0];

Нет нужно просто сделать ссылку (вы даже можете использовать ссылку const для безопасности):

    std::string const&   my_date = my_str_vec[0];
// ^^^^^^

Использование Резерва

Здание строку посимвольно не может быть наиболее эффективным способом получения, дату (строка processed_date может быть изменено на каждой пластине).

    for(my_iter = my_date.begin(); my_iter != my_date.end(); ++my_iter) {
if(std::isalnum(*my_iter) || *my_iter == ' ')
processed_date.append(1,(*my_iter));
}

Использовать резерв, чтобы вы знали, что это не повторно размера внутри, а затем использовать стандартный алгоритм для оптимального цикла:

processed_date.reserve(my_date.size());  // Note: not resize()
std::remove_copy(my_date.begin(), my_date.end(),
std::back_inserter(processed_date),
[](char const& my_char) {return !(std::isalnum(*my_char) || *my_char == ' ');}
);
// Note if you don't have C++0x then lamda is easily replaced by a functor.

Стандартные алгоритмы.

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

http://www.sgi.com/tech/stl/table_of_contents.html
См. раздел 5

Я показал вам, как скопировать все кроме персонажей вы хотите. Но есть один, чтобы удалить все элементы на месте. Я дам тебе эксперимент.

8
ответ дан 4 сентября 2011 в 07:09 Источник Поделиться

Как правило, парсинг на компилируемом языке должен быть ввода/вывода.
Другими словами, это не должно занять гораздо больше времени, чтобы проанализировать файл, чем требуется, чтобы просто скопировать его любым способом.

Вот как мне оптимизировать код.
Я не ищу "медленные процедуры".
Я хочу гораздо более высоком уровне гранулярности.

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

Это очень легко найти их. Все, что вам нужно сделать, это приостановить программу несколько раз, и каждый раз доставайте свои увеличительное стекло и спросить, что он делает и почему.

Не останавливайтесь после того, как вы нашли и исправили только одна проблема.
Поскольку вы уменьшили общее время, другие проблемы Теперь возьмите больший процент времени, и поэтому проще найти.
Поэтому, чем больше вы это делаете, тем больше вы найдете, и каждое ускорение соединений на предыдущие.
Вот как вы действительно сделать это быстро.

4
ответ дан 5 сентября 2011 в 01:09 Источник Поделиться

Untest версию с помощью функции fscanf()

#include <iostream>
#include <stdio.h>

struct ValidDate
{
bool operator()(char c) const { return !(std::isalnum(c) || c == ' ');}
};

int main(int argc, char *argv[])
{
std::string filename;
std::cout << "Please type the file name: " << std::endl;
std::cin >> filename;

std::string ticker;
std::cout << "Please enter the ticker: " << std::endl;
std::cin >> ticker;

std::string ask_filename = ticker + "_ASK.NIT";
std::string bid_filename = ticker + "_BID.NIT";

FILE* instream = fopen(filename.c_str(), "r");
FILE* askstream = fopen(ask_filename.c_str(), "w");
FILE* bidstream = fopen(bid_filename.c_str(), "w");

char part[5][1000];
char term;
int dateLen;
while(feof(instream))
{
term = '\n';
dateLen = 0;
fscanf(instream, "%999[^,\n]%n", part[0], &dateLen);fscanf(instream,"%c", &term);
// Note you do need two fscanf() in case there is an empty cell with no data
if (term == ',') {fscanf(instream, "%999[^,\n]", part[1]);fscanf(instream,"%c", &term);}
if (term == ',') {fscanf(instream, "%999[^,\n]", part[2]);fscanf(instream,"%c", &term);}
if (term == ',') {fscanf(instream, "%999[^,\n]", part[3]);fscanf(instream,"%c", &term);}
if (term == ',') {fscanf(instream, "%999[^,\n]", part[4]);fscanf(instream,"%c", &term);}
// Read the remainder of the line
// Actually need to use two fscans (just in case there is only a '\n' left)
if (term == ',') {fscanf(instream, "%*[^\n]"); fscanf(instream, "%c", &term);}

if (term != '\n')
{ abort(); // something very wrong happened
// like data was too big for a part buffer
}

// If we have reached the EOF then
// something went wrong with one of the reads (probably the first)
// and we should not continue.
if (feof(instream))
{ break;
}

char* newLast = std::remove_if(static_cast<char*>(part[0]), static_cast<char*>(part[0]) + dateLen, ValidDate());
*newLast = '\0';

fprintf(askstream, "%s;%s;%s;\n", part[0], part[1], part[3]);
fprintf(bidstream, "%s;%s;%s;\n", part[0], part[2], part[4]);
}

fclose(askstream);
fclose(bidstream);
}

0
ответ дан 4 сентября 2011 в 08:09 Источник Поделиться

Помните, что СТД::строка может иметь незначащие символы.
Устранить все ненужное копирование. например

my_str_vec.push_back(cell);

std::stringstream lineStream(line);

std::string my_date = my_str_vec[0];

Использовать итераторы везде.

typedef std::vector<std::string::iterator> field_positions;
field_positions field;
std::back_insert_iterator<field_positions> biitfp(field)
bool found = true;

for (std::string::iterator it = date; it != line.end(); ++it) {
if (found) { *biitfp = it; found = false; }
found = ',' == *it;
if (found) *it = 0; // overwrite commas with NULL
}
*biitfp = it; // save the end point too see field.back() below

делать все в копию линии у вас уже есть

std::string::iterator it1 = field[0].begin(), it2 = field[0].begin();
for (;0 != *it2; ++it2)
{
// overwrite bad char with good char
if (std::isalnum(*my_iter) || *my_iter == ' ') *it1++ = *it2;
}
while (it1 != it2) *it1 = 0; // overwrite the left over chars in the field with NULL

int f = 0;
for (field_positions::const_iterator fpit = field.begin(), fpend = field.end();
fpit != fpend; ++fpit, ++f)
{
for (std::string::const_iterator it = *fpit; 0 != *it && it != field.back(); ++it)
{
switch (f) { // single character stream output is FAST
case 0: askstream << *it; bidstream << *it; break;
case 1: case 3: askstream << *it; break;
case 2: case 4: bidstream << *it; break;
default: break;
}
}
askstream << ';';
bidstream << ';';
}
askstream << std::endl; bidstream << std::endl;

0
ответ дан 7 сентября 2011 в 04:09 Источник Поделиться