Типа базового вопроса, но у меня проблемы с поиском решения, поэтому мне нужен толчок в правильном направлении.
У меня есть входной файл, который я втягиваю, и мне нужно поместить его в одну строковую переменную. Проблема в том, что мне нужно разделить эту строку на разные части. Будет 3 строки и 1 int. Они разделены знаком «:».
Я знаю, что могу найти позицию первого ":" с помощью find (), но я действительно не знаю, как продвигаться по строке для каждой вещи и помещать ее в ее собственную строку / int.
Фактический ввод из файла выглядит примерно так:
A:PEP:909:Inventory Item
A будет командой, которую я должен выполнить ... так что это будет строка. PEP - это ключ, он должен быть строкой. 909 - это int.
и последняя строка.
Так что, как мне кажется, я хочу иметь 3 строковых var и 1 int и поместить все эти вещи в соответствующие переменные.
Поэтому я думаю, что в конечном итоге мне захочется преобразовать эту строку C++ в строку C, чтобы я мог использовать atoi для преобразования одного раздела в int.
это не совсем то же самое. в этом случае ему нужно что-то из этого преобразовать. простой цикл getline этого не сделает
Проверьте еще раз по ссылке Мартина. Я разместил свой код там. Это похоже на трехстрочный цикл while для разделения строк. Затем он может просто использовать atol (stringVector [2] .c_str ()) для получения числа. Или atoi (), если он хочет. Или атолл (). Или atoq ().





Со строками в стиле C вы можете использовать strtok () для этого. Вы также можете использовать sscanf ()
Но поскольку вы имеете дело с C++, вы, вероятно, захотите использовать встроенные функции std :: string. Таким образом, вы можете использовать find (). У Find есть форма, которая принимает второй аргумент, который является смещением для начала поиска. Таким образом, вы можете найти (':'), чтобы найти первый экземпляр, а затем использовать find (':', firstIndex + 1), чтобы найти следующие экземпляры, где firstIndex - это значение, возвращаемое первым вызовом find ().
Удобное решение, которое я нашел, не редкость - это следующий прототип:
string SplitToken(string & body, char separator)
который возвращает все до первого появления разделителя и удаляет эту часть, включая разделитель.
«Моя» реализация MFC - CString выглядит следующим образом:
CString SplitStringAt(CString & s, int idx)
{
CString ret;
if (idx < 0)
{
ret = s;
s.Empty();
}
else
{
ret = s.Left(idx);
s = s.Mid(idx+1);
}
return ret;
}
CString SplitToken(CString & s,TCHAR separator)
{
return SplitStringAt(s, s.Find(separator));
}
Это определенно не самый эффективный метод - основным недостатком является то, что изменяется тело и создается новая (частичная) копия для каждого токена, поэтому не используйте его в критичных для производительности местах!
Однако я нашел эту (и несколько связанных с ней функций) чрезвычайно полезной для простых синтаксических анализаторов.
Взгляните на boost :: tokenizer.
Лучше всего это сделать с помощью std::getline и std::istringstream, если вы хотите использовать стандартную библиотеку C++:
std::string command;
std::string key;
int id;
std::string item;
std::string line = "A:PEP:909:Inventory Item";
// for each line:
std::istringstream stream(line);
std::getline(stream, command, ':');
std::getline(stream, key, ':');
stream >> id;
std::getline(stream, item);
// now, process them
Подумайте о том, чтобы поместить его в собственную структуру:
struct record {
std::string command;
std::string key;
int id;
std::string item;
record(std::string const& line) {
std::istringstream stream(line);
stream >> *this;
}
friend std::istream& operator>>(std::istream& is, record & r){
std::getline(is, r.command, ':');
std::getline(is, r.key, ':');
stream >> r.id;
std::getline(is, r.item);
return is;
}
};
Следите за тем, чтобы вы не перешли на следующую строку, когда попали в неверно сформированную запись с> 3 или <3 двоеточиями.
да, предполагается, что линии правильные. теоретически ключ может также содержать '\ n', который затем будет охватывать следующую строку.
или вы просто сначала извлекаете всю строку, а затем используете ctor записи. это действительно не будет распространяться на следующие строки.
Просто комментарий, оператор >> не нужно объявлять другом. Нет закрытых членов, поэтому это может быть внешняя бесплатная функция.
dribeas, это уже бесплатная функция. но он не может быть вызван напрямую и работает только для поиска koenig (ADT) (см. «Внедрение имени друга»). Мне нравится использовать здесь определение друга, так как оно масштабируется, когда вы делаете участников закрытыми.
Обычно я использую что-то вроде этого:
void split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
}
вы можете использовать это так:
std::vector<std::string> tokens;
split("this:is:a:test", ':', tokens);
токены теперь будут содержать "this", "is", "a" и "test"
просто и не нужно добавлять другую библиотеку. Идеально для меня.
+1 мое предпочтительное решение, не возиться с find () и индексами.
Должен полюбоваться отрицательный голос за пост годичной давности, который отлично работает ...
Спрашивали и отвечали несколько раз: stackoverflow.com/questions/53849/…