Токенизация потока строк на основе типа
У меня есть входной поток, содержащий целые числа и символы специального значения '#'. Это выглядит следующим образом:... 12 18 16 # 22 24 26 15 # 17 # 32 35 33 ...
Жетоны разделены пробелом. Там нет шаблона для позиции "#".
Я пытался токенизировать поток ввода следующим образом:
int value;
std::ifstream input("data");
if (input.good()) {
string line;
while(getline(data, line) != EOF) {
if (!line.empty()) {
sstream ss(line);
while (ss >> value) {
//process value ...
}
}
}
}
Проблема с этим кодом заключается в том, что обработка останавливается, когда встречается первый символ "#".
Единственное решение, которое я могу придумать, - это извлечь каждый отдельный токен в строку (не '#') и использовать функцию atoi() для преобразования строки в целое число. Однако это очень неэффективно, так как большинство токенов целочисленные. Вызов atoi() для токенов приводит к большим накладным расходам.
Можно ли как-то разобрать отдельный токен по его типу? то есть, для целых чисел, проанализируйте его как целые числа, а для '#' пропустите. Спасибо!
6 ответов
Одна из возможностей была бы явно пропустить пробел (ss >> std::ws
), а затем использовать ss.peek()
чтобы узнать, если #
следующим образом. Если да, используйте ss.get()
читать и продолжать, в противном случае используйте ss >> value
чтобы прочитать значение.
Если позиции #
не имеет значения, вы также можете удалить все '#'
от линии до инициализации stringstream
с этим.
Обычно не стоит проверять на хорошее ()
if (input.good()) {
Если ваша следующая операция не генерирует сообщение об ошибке или исключение. Если это не хорошо, все дальнейшие операции все равно будут неудачными.
Не проверяйте против EOF
,
while(getline(data, line)!= EOF) {
Результат std::getline() не является целым числом. Это ссылка на входной поток. Входной поток можно преобразовать в объект типа bool, который можно использовать в контексте bool (например, while
if
так далее..). Так что вы хотите сделать:
while(getline(data, line)) {
Я не уверен, что прочитал бы строку. Вы можете просто прочитать слово (так как ввод разделен пробелом). Использование оператора >> в строке
std::string word;
while(data >> word) { // reads one space separated word
Теперь вы можете проверить слово, чтобы увидеть, если это ваш специальный символ:
if (word[0] == "#")
Если не конвертировать слово в число.
Вот что я бы сделал:
// define a class that will read either value from a stream
class MyValue
{
public:
bool isSpec() const {return isSpecial;}
int value() const {return intValue;}
friend std::istream& operator>>(std::istream& stream, MyValue& data)
{
std::string item;
stream >> item;
if (item[0] == '#') {
data.isSpecial = true;
} else
{ data.isSpecial = false;
data.intValue = atoi(&item[0]);
}
return stream;
}
private:
bool isSpecial;
int intValue;
};
// Now your loop becomes:
MyValue val;
while(file >> val)
{
if (val.isSpec()) { /* Special processing */ }
else { /* We have an integer */ }
}
Может быть, вы можете прочитать все значения как std::string, а затем проверить, если это "#" или нет (а если нет - конвертировать в int)
int value;
std::ifstream input("data");
if (input.good()) {
string line;
std::sstream ss(std::stringstream::in | std::stringstream::out);
std::sstream ss2(std::stringstream::in | std::stringstream::out);
while(getline(data, line, '#') {
ss << line;
while(getline(ss, line, ' ') {
ss2 << line;
ss2 >> value
//process values ...
ss2.str("");
}
ss.str("");
}
}
Здесь мы сначала разбиваем строку на токен '#' в первом цикле while, затем во втором цикле while мы разделяем строку на ' '.
Лично, если ваш разделитель всегда будет пробелом, независимо от того, что следует, я бы порекомендовал вам просто взять ввод как строку и проанализировать оттуда. Таким образом, вы можете взять строку, посмотреть, является ли это число или # и еще много чего.
Я думаю, что вы должны пересмотреть свою предпосылку, что "вызов atoi() для токенов влечет за собой большие накладные расходы-"
Там нет магии, чтобы std::cin >> val
, Под капотом он в конечном итоге вызывает (что-то очень похожее) Atoi.
Если ваши токены огромны, возможно, для создания std::string
но, как вы говорите, подавляющее большинство чисел (а остальные #), поэтому они должны быть в основном короткими.