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