С++ 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"

Ответ 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));
}

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

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