С++ Strings Modifying and Extracting на основе разделителей
Вид основного вопроса, но у меня возникают проблемы с решением проблемы, поэтому мне нужно толчок в правильном направлении.
У меня есть входной файл, который я втягиваю, и я должен помещать его в одну строковую переменную. Проблема в том, что мне нужно разбить эту строку на разные вещи. Будет 3 строки и 1 int. Они разделены символом ":".
Я знаю, что я могу найти позицию первого ":" методом find(), но я действительно не знаю, как продвигаться по строке, для каждой вещи и помещать ее в ее собственную строку /int.
Фактический ввод из файла выглядит примерно так:
A:PEP:909:Inventory Item
A будет командой, которую я должен выполнить... так что это будет строка.
PEP - это ключ, который должен быть строкой.
909 - это int.
а последняя - строка.
Так что я думаю, что хочу сделать, это иметь 3 строки var и 1 int и получить все эти вещи, помещенные в соответствующие переменные.
Итак, я думаю, что мне захочется преобразовать эту строку С++ в строку C, чтобы я мог использовать atoi для преобразования одного раздела в int.
Ответы
Ответ 1
С помощью строк стиля C вы можете использовать strtok() для этого. Вы также можете использовать sscanf()
Но поскольку вы имеете дело с С++, вы, вероятно, захотите придерживаться встроенных функций std::string. Таким образом, вы можете использовать find(). Найти имеет форму, которая принимает второй аргумент, который является смещением для начала поиска. Таким образом, вы можете найти (':'), чтобы найти первый экземпляр, а затем использовать find (':', firstIndex + 1), чтобы найти следующие экземпляры, где firstIndex - это значение, возвращаемое первым вызовом find().
Ответ 2
Я обычно использую что-то вроде этого:
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"
Ответ 3
Взгляните на boost:: tokenizer.
Ответ 4
Это лучше всего использовать с помощью std::getline
и std::istringstream
, если вы хотите использовать стандартную библиотеку С++:
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;
}
};
Ответ 5
Удобное решение, которое я нашел, не редкость - это следующий прототип:
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));
}
Это определенно не самый эффективный метод. Основной недостаток заключается в том, что тело модифицировано и для каждого токена создается новая (частичная) копия, поэтому не используйте ее в критическом для производительности месте!
Однако я нашел это (и несколько связанных функций) чрезвычайно полезным для простых парсеров.