2011-12-30 4 views
0

Есть мой исходный код, загружающий текстовый файл и разделяющий каждую строку на отдельные элементы (слова).C++ Загрузить текстовый файл, оптимизация

Как оптимизировать код? Тестирование пустых строк (и другие конструкции) являются (на мой взгляд) немного неэффективного ....

typedef std::vector <std::string> TLines; 
typedef std::vector < std::vector <std::string> > TItems; 

TItems TFloadFile (const char * file_name) 
{ 
    //Load projection from file 
    unsigned int lines = 0; 
    char buffer[BUFF]; 
    FILE * file; 
    TItems file_words; 
    TLines file_lines; 


    file = fopen (file_name, "r"); 

    if (file != NULL) 
    { 
      for (; fgets (buffer, BUFF, file);) 
      { 
        //Remove empty lines 
        bool empty_line = true; 
        for (unsigned i = 0; i < strlen (buffer); i++) 
        { 
          if (!isspace ((unsigned char) buffer[i])) 
          { 
            empty_line = false; 
            break; 
          } 
        } 

        if (!empty_line) 
        { 
          file_lines.push_back (buffer); 
          lines++; 
        } 
      } 


      file_words.resize (lines + 1); 
      for (unsigned int i = 0; i < lines; i++) 
      { 
        char * word = strtok (const_cast<char *> (file_lines[i].c_str()), " \t,;\r\n"); 
        for (int j = 0; word; j++, word = strtok (0, " \t;\r\n")) 
        { 
          file_words[i].push_back (word); 
        } 
      } 

      fclose (file); 
    } 

    return file_words; 
} 

Спасибо за вашу помощь ...

+2

codereview.stackexchange.com - хороший форум для такого рода вопросов. –

+0

@Paul. Спасибо за ваш комментарий! – justik

+0

@ Все: Спасибо за ваши мнения, комментарии, замечания и образцы кода ... – justik

ответ

1

Перед тем, как оптимизировать, вы можете объяснить, насколько велик файл, как долго выполняется код для выполнения и почему вы считаете, что он еще не привязан к IO (т. Е. Из-за скорости жесткого диска). Как долго вы думаете, что это должно было произойти? Некоторое представление о типе данных в файле было бы неплохо (например, средняя длина строки, средняя доля пустых строк и т. Д.).

Таким образом, объедините цикл delete-empty-line с циклом слова-токенизации. Затем вы можете полностью удалить TLines и избежать конструкций std :: string и векторного отталкивания. Я не проверял этот код, но он должен быть достаточно близко, чтобы дать вам эту идею. Она также включает в себя более эффективную пустую строку корректировщик:

if (file != NULL) 
{ 
    for (; fgets (buffer, BUFF, file);) 
    { 
     bool is_empty = true; 
     for (char *c = buffer; *c != '\0'; c++) 
     { 
      if (!isspace(c)) 
      { 
       is_empty = false; 
       break; 
      } 
     } 

     if (is_empty) 
      continue; 

     file_words.resize (lines + 1); 
     char * word = strtok (buffer, " \t,;\r\n"); 
     for (int j = 0; word; j++, word = strtok (0, " \t;\r\n")) 
     { 
       file_words[i].push_back (word); 
     } 

     lines++; 
    } 

    fclose (file); 
} 
0

Для одного

file_lines.push_back (buffer); 

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

Если вы абсолютно в необходимости использования вектора для этой цели, то вы должны использовать некоторый экспоненциальный прирост вместо этого, как:

if(file_lines.size()<=lines){ 
    file_lines.resize((int)(lines * 1.3 + 1)); 
} 

Таким образом, вы будете иметь гораздо меньше ресурсоемкие .resize() операции, для стоимость минимальных затрат на потребление памяти.

+0

Почему вообще не изменяются? Пусть вектор сделает это для себя - он будет использовать экспоненциальные приращения. – Mark

+1

Вы можете использовать.reserve() вместо .resize(). – Notinlist

+0

@Mark: Не знал об этом. Справка? – Notinlist

2

Линия for (unsigned i = 0; i < strlen (buffer); i++) довольно неэффективна, так как вы каждый раз вычисляете длину buffer. Однако, возможно, это будет оптимизировано компилятором.

Вы нажимаете предметы на свои std::vector с без reserve() в любом месте. Для большого файла это потребует много накладных расходов, поскольку содержимое векторов нужно будет скопировать, чтобы изменить их размер. Я просто прочитал ответ @ Notinlist, который уже говорит о неэффективности std::vector::resize().

Вместо чтения каждой строки в вектор с помощью повторных вызовов fgets(), вы не могли бы просто определить количество байтов в файле, динамически выделять char массив для хранения их, а затем сбросить байты в него? Затем вы можете разбирать слова и хранить их в file_words. Это будет более эффективно, чем метод, который вы используете в настоящее время.

+1

«Однако, возможно, это будет оптимизировано компилятором». Компилятор должен был бы знать, что эта функция не имеет побочных эффектов, поэтому в целом это неверно (если компилятор не проводит анализ времени-ссылки) – Kamchatka

+0

@ Камчатка: Достаточно справедливо, спасибо. Тем более разумно сохранить это значение в переменной и ссылке, вместо того, чтобы пересчитывать ее для каждого прохода. –

0

УСНО и превращали использовать std::list вместо std::vector.

typedef std::list< std::list<std::string> > TItems; 

TItems TFloadFile (const char * file_name) 
{ 
    using namespace std; 
    //Load projection from file 
    ifstream file(file_name); 
    TItems file_words; 
    string line; 

    for(getline(file,line); !file.fail() && !file.eof(); getline(file,line)) 
    { 
     file_words.push_back(list<string>()); 
     list<string> &words(file_words.back()); 

     char *word = strtok((char*)line.c_str(), " \t,;\r\n"); 
     for(; word; word=strtok(0, " \t;\r\n")) 
     { 
      words.push_back(word); 
     } 
     if(!words.size()) 
      file_words.pop_back(); 
    } 
    return file_words; 
} 
Смежные вопросы