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