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

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

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 но, как вы говорите, подавляющее большинство чисел (а остальные #), поэтому они должны быть в основном короткими.

Другие вопросы по тегам