Также в C ++ легко разделить строку. Я уже дал здесь несколько ответов о том, как разбить строку. В любом случае, я объясню это здесь подробно и для вашего особого случая. Позже я также предоставлю полный рабочий пример.
Мы используем базовую c функциональность std::getline
, которая может читать всю строку или строку до заданного символа. См. здесь .
Давайте рассмотрим пример. Если текст хранится в std::string
, мы сначала поместим его в std::istringstream
. Затем мы можем использовать std::getline
для извлечения данных из std::istringstream
. Это всегда стандартный подход. Сначала прочтите всю строку из файла, используя std::getline
, затем снова поместите ее в std::istringstream
, чтобы снова можно было извлечь части строки с помощью std::getline
.
Если исходная строка выглядит так:
Time [s]: 1
Мы можем заметить, что у нас есть несколько частей:
- идентификатор «Time [s]»,
- двоеточие, которое действует как разделитель,
- один или несколько пробелов и
- значение «1»
Итак, мы могли бы написать что-то вроде этого:
std::string line{}; // Here we will store a complete line read from the source file
std::getline(configFileStream, line); // Read a complete line from the source file
std::istringstream iss{ line }; // Put line into a istringstream for further extraction
std::string id{}; // Here we will store the target value "id"
std::string value{}; // Here we will store the target "value"
std::getline(iss, id, ':'); // Read the ID, get read of the colon
iss >> std::ws; // Skip all white spaces
std::getline(iss, value); // Finally read the value
Итак, это много текста. Возможно, вы слышали, что можно связать операции ввода-вывода, как в std::cout << a << b << c
. Это работает, потому что операция << всегда возвращает ссылку на данный поток. То же самое и с <code>std::getline. И поскольку он это делает, мы можем использовать вложенные операторы. Это означает, что мы можем поместить второй std::getline
в эту позицию параметра (фактически первый параметр), где он ожидает std::istream
. Следовательно, если мы будем следовать этому подходу, мы можем написать вложенный оператор:
std::getline(std::getline(iss, id, ':') >> std::ws, value);
Ой, что здесь происходит? Давайте проанализируем изнутри. Сначала операция std::getline(iss, id, ':')
извлекает строку из std::istringstream
и присваивает ее переменной «id». Хорошо понял. Помните: std :: getline вернет ссылку на данный поток. Итак, приведенный выше сокращенный оператор:
std::getline(iss >> std::ws, value)
Далее, iss >> std::ws
будет вычислен и приведет к поглощению всех ненужных пробелов. И угадайте, что он вернет ссылку на поток gievn "iss".
Теперь оператор выглядит так:
std::getline(iss, value)
И это будет читать значение. Просто.
Но мы еще не закончили. Конечно, std :: getline снова вернет "iss". И в приведенном ниже коде вы увидите что-то вроде
if (std::getline(std::getline(iss, id, ':') >> std::ws, value))
, что в итоге будет if (iss)
. Итак, мы используем iss
как логическое выражение? Почему это работает и для чего это нужно? Это работает, потому что bool operator
из std::stream
перезаписывается и возвращается, если состояние в порядке или имеет сбой. См. здесь для объяснения. Всегда проверяйте результат любой операции ввода-вывода.
И последнее, но не менее важное: нам нужно объяснить оператор if
с инициализатором. Вы можете прочитать об этом здесь .
Я могу написать
if (std::string id{}, value{}; std::getline(std::getline(iss, id, ':') >> std::ws, value)) {
, что похоже на
std::string id{}, value{};
if (std::getline(std::getline(iss, id, ':') >> std::ws, value)) {
Но в первом примере преимущество в том, что определенные переменные будут видны только в пределах if
-statements. Итак, мы «ограничиваем» переменную как можно более узкой.
Вам следует стараться делать это как можно чаще. Вы также должны всегда проверять состояние возврата операции ввода-вывода, применяя if
к операции с потоком, как показано выше.
Полная программа для чтения всего будет состоять из нескольких строк кода.
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <iomanip>
int main() {
// Open config file and check, if it coul be opened
if (std::ifstream configFileStream{ "r:\\config.txt" }; configFileStream) {
// Here we wills tore the resulting config data
std::unordered_map<std::string, std::string> configData;
// Read all lines of the source file
for (std::string line{}; std::getline(configFileStream, line); )
{
// If the line contains a colon, we treat it as valid data
if (if (line.find(':') != std::string::npos)) {
// Split data in line into an id and a value part and save it
std::istringstream iss{ line };
if (std::string id{}, value{}; std::getline(std::getline(iss, id, ':') >> std::ws, value)) {
// Add config data to our map
configData[id] = value;
}
}
}
// Some debug output
for (const auto& [id, value] : configData)
std::cout << "ID: " << std::left << std::setw(35) << id << " Value: " << value << '\n';
}
else std::cerr << "\n*** Error: Could not open config file for reading\n";
return 0;
}
В этом примере я сохраняю идентификаторы и значения на карте, чтобы к ним можно было легко получить доступ.