2010-01-07 2 views
4

У меня есть большой файл, где каждая строка содержит целые числа, разделенные пробелами. Задача состоит в том, чтобы разрезать этот файл по очереди. Для строки для преобразования Int У меня есть три решения:преобразовать строку в int

static int stringToIntV1(const string& str) { 
    return (atoi(str.c_str())); 
} 

Однако, если я прохожу искаженной строки, он не производит каких-либо ошибок. Например строка «123error» преобразуется в 123.

Второе решение:

static int stringToIntV2(const string& str) 
{ 
    int result; 
    istringstream myStream(str); 

    if (myStream >> result) { 
     return result; 
    } 
    // else 
    throw domain_error(str + " is not an int!"); 
} 

У меня такая же проблема здесь, деформированные строки не вызывают ошибку.

Третье решение с усилением (находится в Boost Library):

static int stringToIntV3(const string& str) 
{ 
    int iResult = 0; 
    try { 
     iResult = lexical_cast<int>(str); 
    } 
    catch(bad_lexical_cast &) { 
     throw domain_error(str + " is not an int!"); 
    } 
    return iResult; 
} 

Это один дает правильный результат.

Однако существует значительная разница во времени выполнения. Тестирование на большой текстовый файл (32 Мб), я получил следующие времена:

  • (1) с atoi: 4.522s (победитель)
  • (2) с istringstream: 15.303s (очень медленно)
  • (3) с lexical_cast: 10.958s (между два)

Моим вопросом: вы знаете, как заметить ТЗ сформированные струны с атой? Это даст самое быстрое решение. Или вы знаете лучшее решение?

Обновление: Спасибо за ответы. Следуя советам, я пришел с этим решением:

static int stringToIntV4(const string& str) 
{ 
    char * pEnd; 
    const char * c_str = str.c_str(); 
    int result = strtol(c_str, &pEnd, 10); 
    if (pEnd == c_str+str.length()) { 
     return result; 
    } 
    // else 
    throw domain_error("'" + str + "'" + " is not an int!"); 
} 

Хорошая новость заключается в том, что она дает, если есть проблема и столь же эффективным, как версия atoi.

+1

Посмотрите на strtol() в своем руководстве. – 2010-01-07 20:51:29

+0

@Martin: Да, я использую 'g ++ -O3'. – Jabba

+0

Вы включили оптимизатор? Кроме того, просмотр функции преобразования из контекста затрудняет определение правильности выполнения остальной части кода. V2 также должен дать ошибку (при следующей попытке прочитать), поэтому он предполагает, что вы выполняете другой синтаксический анализ, и это может быть причиной вашей медлительности. Также есть ли определенное требование для чтения строки за строкой или вы можете просто перевести целые числа в вектор? –

ответ

13

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

Изменить: насколько скорость идет, я ожидаю, что она будет немного медленнее, чем atoi, но быстрее, чем другие, которые вы пробовали.

+0

Разумеется, но если бы я выполнял atoi(), я бы, вероятно, сделал это с точки зрения strtol() - но тогда я ленив :-) – 2010-01-07 21:01:25

+0

@Neil: Если бы я начинал с нуля, я бы, наверное, сделайте то же самое - но я бы предположил, что в большинстве библиотек 'atoi' работал много лет, прежде чем' strtol' даже мечтал ... –

+2

Реализация glibc 'atoi' в терминах' strtol'. – user7116

1

Можете ли вы просто проверить строку до, называя atoi?

isdigit() - замечательная небольшая функция, и у вас не должно возникнуть проблемы с записью isalldigits(), чтобы проверить строку в узкой, быстрой петле.

(если вам случится работать с десятичными знаками или знаками +/-, вы можете добавить, что в тоже)

Таким образом, просто убедитесь, что строка все цифры, а затем вызвать atoi. Должно быть довольно быстро.

+1

«просто убедитесь, что строка - это все цифры, а затем вызовите atoi». Вы должны увидеть, что это ненужная работа. Почему бы не разобрать номера, как вы идете? – GManNickG

+0

@GMan: Это то, что делает 'strtol'. –

+0

... правый. Вот почему я говорю, что его предложение бесполезно. – GManNickG

0

Любые ведущие нули в цифрах? если atoi возвращает ноль, а первый цифровой символ числа не равен «0», тогда мы имеем ошибку.

+2

Пример счетчика: -0 ​​ – 2010-01-07 21:04:31

+0

С тех пор как -0 действительная целочисленная константа? В лучшем случае это выражение. –

1

Я сделал свой собственный небольшой тест. Кажется, что примерно столько же времени для использования atoi(), как и для операторов потока. У меня есть файл с 2 000 000 номеров.10 номеров в каждой строке (хотя код не использует этот факт).

В первой версии используется atoi(), который, как я признаю, занял у меня некоторое время, чтобы получить правильное решение и может быть более эффективным. Обновления принимаются и становятся более эффективными.

Поток один. Взял 20 секунд, чтобы писать и работать из коробки.
Сроки Результаты:

> time ./a.exe 1 
AtoI() 
6.084u 0.156s 0:06.33 98.4%  0+0k 0+0io 8619pf+0w 
> time ./a.exe 
Iterator() 
4.680u 0.296s 0:05.05 98.4%  0+0k 0+0io 6304pf+0w 

Код:

#include <vector> 
#include <iostream> 
#include <iterator> 
#include <fstream> 
#include <iostream> 

int main(int argc,char* argv[]) 
{ 
    std::vector<int> data; 
    std::ifstream  vals("rand.txt"); 

    if (argc > 1) 
    { 
     std::cout << "AtoI()\n"; 

     std::string line; 
     while(std::getline(vals,line)) 
     { 
      std::string::size_type loop = 0; 
      while(loop < line.size()) 
      { 
       while(isspace(line[loop]) && (loop < line.size())) 
       { ++loop; 
       } 
       std::string::size_type end = line.find_first_not_of("",loop); 
       data.push_back(atoi(line.substr(loop,end - loop).c_str())); 

       loop = end; 
      } 

     } 
    } 
    else 
    { 
     std::cout << "Iterator()\n"; 
     std::copy( std::istream_iterator<int>(vals), 
        std::istream_iterator<int>(), 
        std::back_inserter(data) 
       ); 
    } 
} 
0

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

На мой взгляд, вы должны сделать это:

// somewhere in your code 
ifstream fin("filename"); 
if(!fin) { 
// handle error 
} 

int number; 
while(getNextNumber(fin, number)) 
{ 
cout << number << endl; 
} 
fin.close(); 
// done 

// some function somewhere 
bool getNextNumber(ifstream& fin, int& number) 
{ 
while(!(fin >> number)) 
{ 
    fin.clear(); 
    fin.ignore(numeric_limits<streamsize>::max(),'\n'); 
} 

if(!fin || fin.eof()) 
    return false; 
return true; 
} 
+0

На самом деле, моя функция нуждается в некоторой tweeking, но вы получаете идею, я думаю. Я думаю, что это может сделать бесконечный цикл. – Daniel

2

Функция strtol возвращает вам указатель на следующий символ в строке. Вы можете проверить этот символ после преобразования, чтобы увидеть, не является ли это пробелом, указав ошибку. Я провел простой тест, и производительность strtol кажется сопоставимой с atoi.

2

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

#include <ctype.h> 
#include <string> 

inline int customStringToInt(const std::string &str) { 
    register const char *p = str.c_str(), *pEnd = p + str.size(); 

    // Remove leading whitespace (remove if no leading whitespace). 
    while(p != pEnd && isspace(*p)) ++p; 

    // Handle neg/pos sign (remove if no sign). 
    int sign = 1; 
    if(p != pEnd) { 
    if(  *p == '-') { ++p; sign = -1; } 
    else if(*p == '+') { ++p; } 
    } 

    // String without any digits is not OK (blank != 0) (remove if it is OK). 
    if(p == pEnd) throw domain_error("'" + str + "'" + " has no digits."); 

    // Handle digits. 
    register int i = 0; 
    while(p != pEnd) 
    if(isdigit(*p)) i = i * 10 + (*p++ - '0'); 
    else throw domain_error("'" + str + "'" + " contains non-digits."); 

    return sign * i; 
} 
Смежные вопросы