Токенизация потока строк на основе типа

У меня есть входной поток, содержащий целые числа и символы специального значения ‘#’. Это выглядит следующим образом:
... 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 () для токенов приводит к большим накладным расходам.

Можно ли как-то разобрать отдельный токен по его типу? то есть, для целых чисел, проанализируйте его как целые числа, а для ‘#’ пропустите. Спасибо!

1

Решение

Одна из возможностей была бы явно пропустить пробел (ss >> std::ws), а затем использовать ss.peek() чтобы узнать, если # следующим образом. Если да, используйте ss.get() читать и продолжить, в противном случае используйте ss >> value чтобы прочитать значение.

Если позиции # не имеет значения, вы также можете удалить все '#' от линии до инициализации stringstream с этим.

2

Другие решения

Обычно не стоит проверять на хорошее ()

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 */ }
}
2

Может быть, вы можете прочитать все значения как std :: string, а затем проверить, является ли это «#» или нет (а если нет — преобразовать в int)

1

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 мы разделяем строку на ».

1

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

0

Я думаю, вы должны пересмотреть свою предпосылку, что «вызов atoi () для токенов влечет за собой большие накладные расходы»

Там нет магии, чтобы std::cin >> val, Под капотом он в конечном итоге вызывает (что-то очень похожее) Atoi.

Если ваши токены огромны, возможно, для создания std::string но, как вы говорите, подавляющее большинство — это числа (а остальные — #), поэтому они в основном должны быть короткими.

0
По вопросам рекламы [email protected]