2015-11-23 6 views
2

Мне нужна функция strtol, которая позволила бы мне определить границы, в которых будет разбираться строка. Например,C++ parse sub-string to integer

char * number = "123456789"; 
std::cout << my_strtol(number, 1, 3); 

Следует напечатать «234».


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

int32_t my_strtol(const char* str, int from_char, int to_char, int base = 10) 
{ 
    char *end; 
    auto res = strtol(str + from_char, &end, base); 
    auto extra_digits = end - str - (to_char + 1); 
    if(extra_digits > 0) res /= pow(10, extra_digits); 
    return res; 
} 

это, однако он терпит неудачу, если строка больше LONG_MAX (независимо от стоимости указанной части). Например, для ввода "123456789" вызов str_to_l(number, 1, 3) завершился с ошибкой и вернул 0. Более того, он использует ненужные pow - производительность имеет решающее значение.

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


Для тех, кто заинтересован в (случайных) профилирующих результатов предложенных решений:

//put temp null character at end 
int32_t my_strtol1(char* str, int from_char, int to_char, int base = 10) 
{ 
    char tmp = str[to_char]; 
    str[to_char] = '\0'; 
    auto res = strtol(str + from_char, nullptr, base); 
    str[to_char] = tmp; 
    return res; 
} 

//use substr() 
int32_t my_strtol2(const std::string& str, 
    const std::string::size_type from_char, 
    const std::string::size_type to_char, 
    int base = 10) { 
    return std::stol(str.substr(from_char, to_char - from_char + 1)); 
} 

//using boost 
int32_t my_strtol3(char* str, int from_char, int to_char) { 
    return boost::lexical_cast<int>(str + from_char, to_char - from_char + 1); 
} 

//parse characters one by one 
int32_t my_strtol4(const char* str, int from_char, int to_char) 
{ 
    int32_t res = 0; 
    for (int i = from_char; i < to_char; i++) 
    { 
     char ch = str[i]; 
     ch -= '0'; 
     if (ch > 10 || ch < 0) return 0; 
     res *= 10; 
     res += ch; 
    } 
    return res; 
} 

Выход (измеряется clock_t) на моей машине было:

Manipulating null character with 100000 iterations took 0.114s 
Using substr() with 100000 iterations took 0.62s 
Using boost::lexical_cast<T>() with 100000 iterations took 0.231s 
Parsing character one by one with 100000 iterations took 0.083s 
+2

'std :: stol' - это версия C++ 11 C-strtol. –

+0

Вы пробовали отладить его? Какие шаги вы предприняли? Что вы ожидаете от вызова strtol? Подсказка: он попытается преобразовать from_char, пока не увидит нулевой терминатор. Попробуйте ['std :: string'] (http://en.cppreference.com/w/cpp/string/basic_string) вместо' char * 'и используйте [' std :: stol'] (http: // ru.cppreference.com/w/cpp/string/basic_string/stol) на ['.substr()'] (http://en.cppreference.com/w/cpp/string/basic_string/substr). – Andrew

+0

@MatsPetersson благодарит за комментарий, но неважно, является ли ввод строкой с нулевым завершением в стиле c или std :: string (я могу выбрать, какой тип буфера символов использовать). – wondra

ответ

3

Если у вас есть Повысьте, это может быть сделано как однострочник с использованием boost::lexical_cast, который имеет перегрузку, которая принимает указатель char и длину в качестве аргументов. Из всего, что я знаю, это оптимизировано (специализировано) для работы непосредственно на входной строке без каких-либо копий или распределений памяти, и никаких потоков не задействовано.

#include <iostream> 
#include <boost/lexical_cast.hpp> 

int 
main() 
{ 
    const auto text = "123456789"; 
    const auto number = boost::lexical_cast<int>(text + 1, 3); 
    std::cout << number << '\n'; 
} 

Выход:

234 

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

+0

Профилирование результатов показывает, что это решение является элегантным, безопасным и достаточно быстрым, я выбираю это как решение из-за простоты во всем мире. – wondra

+0

@wondra Я только что видел ваш профиль. Хорошая работа! – 5gon12eder

4

Итак, если вам не нужна супер-переносимость (например, поддержка тайских, китайских и арабских чисел):

int32_t my_strtol(const char* str, int from_char, int to_char, int base = 10) 
{ 
    int32_t res = 0; 
    for(int i = from_char; i < to_char; i++) 
    { 
     char ch = str[i]; 
     if (ch > '9' && base > 10) 
     { 
     ch &= ~32; /* Make it upper case */ 
     ch -= 'A' + 10; 
     } 
     else 
     { 
      ch -= '0'; 
     } 
     if (ch > base || ch < 0) ... do some error handling ... 
     res *= base; 
     res += ch; 
    } 
    return res; 
} 
+0

Извините за вводящий в заблуждение пример и не упомянув об этом - я ожидаю, что только строка ASCII ввода будет содержать 10 баз. Я только написал свой тестовый пример более общий, потому что он не усложнил код. – wondra

+0

@wondra достаточно легко адаптировать решение Mats. Просто умножьте на 10 вместо «base» и удалите обработку для символов, отличных от цифр. Разумеется, вы все равно должны провести некоторую проверку диапазона для строковых индексов. – Andrew

+1

Единственное, что можно добавить, это тип данных для 'res' –