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"

Взгляните на 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;
    }
};

Удобное решение, которое я нашел, не редкость - это следующий прототип:

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));
}

Это определенно не самый эффективный метод - главный недостаток заключается в том, что тело модифицируется, и для каждого токена создается новая (частичная) копия, поэтому не используйте его в местах, критичных к производительности!

Тем не менее, я нашел это (и несколько связанных функций) чрезвычайно полезным для простых парсеров.

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