В вашем сообщении есть 2 вопроса:
- Как мне разобрать этот файл в cpp?
- Есть ли функция разделения, как в Java, поэтому я можно разделить и сохранить все как токены?
Я отвечу на оба вопроса и покажу демонстрационный пример.
Начнем с разбиения строки на токены. Есть несколько возможностей. Начнем с простых.
Поскольку токены в вашей строке разделены пробелами, мы можем воспользоваться функциональностью оператора экстрактора (>>). Это будет считывать данные из входного потока до пробела, а затем преобразует эти считанные данные в указанную переменную. Вы знаете, что эта операция может быть прикована цепью.
Тогда для примера строки
const std::string line{ "Token1 Token2 Token3 Token4" };
вы можете просто поместить это в std::istringstream
и затем извлечь переменные из потока:
std::istringstream iss1(line);
iss1 >> subString1 >> subString2 >> subString3 >> subString4;
Недостаток в том, что вам нужно написать много вещей и знать количество элементов в строке.
Мы можем преодолеть эту проблему с помощью вектора в качестве хранилища данных taget и заполнить его конструктором диапазона , Конструктор диапазона векторов берет начальный и конечный интегратор и копирует в него данные.
В качестве итератора мы используем std::istream_iterator
. Проще говоря, вызывать оператор экстрактора (>>), пока все данные не будут использованы. Какое бы количество данных у нас не было.
Это будет выглядеть следующим образом:
std::istringstream iss2(line);
std::vector token(std::istream_iterator<std::string>(iss2), {});
Это может выглядеть сложно, но это не так. Мы определяем переменную «токен» типа std::vector
. Мы используем его конструктор диапазона.
И мы можем определить std :: vector без аргумента шаблона. Компилятор может вывести аргумент из заданных параметров функции. Эта функция называется CTAD («дедукция аргумента шаблона класса», требуется C ++ 17).
Кроме того, вы можете видеть, что я не использую итератор end () явно.
Этот итератор будет создан из пустого инициализатора по умолчанию, заключенного в фигурные скобки, с правильным типом, потому что он будет выведен таким же, как тип первого аргумента, поскольку конструктор std :: vector требует этого.
Есть дополнительное решение. Это самое мощное решение и, следовательно, может быть немного сложным в начале.
При этом можно избежать использования std :: istringstream и напрямую преобразовать строку в токены с помощью std :: sregex_token_iterator. Очень прост в использовании. В результате получается один слой для разделения исходной строки:
std::vector<std::string> token2(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
Итак, современный C ++ имеет встроенную функциональность, которая точно разработана для целей токенизации строк. Это называется std::sregex_token_iterator
. Что это за вещь?
Как следует из названия, это итератор. Он будет перебирать строку (отсюда и «s» в ее имени) и возвращать токены разделения. Токены будут сопоставлены снова с регулярным выражением. Или изначально разделитель будет сопоставлен, а остальные будут считаться токеном и возвращаться. Это будет контролироваться с помощью последнего флага в его конструкторе.
Давайте посмотрим на этот конструктор:
token2(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
Первый параметр, где он должен начинаться в исходной строке, 2-й параметр - это конечная позиция, до которой должен работать итератор. Последний параметр:
- 1, если вы хотите иметь положительное совпадение с регулярным выражением
- -1, вернет все, что не соответствует регулярному выражению
И последнее, но не менее важное - само регулярное выражение. Пожалуйста, прочитайте в net о регулярных выражениях. Есть тонны доступных страниц.
Пожалуйста, ознакомьтесь с демонстрацией для всех 3 решений здесь:
#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <sstream>
#include <iterator>
#include <algorithm>
/// Split string into tokens
int main() {
// White space separated tokens in a string
const std::string line{ "Token1 Token2 Token3 Token4" };
// Solution 1: Use extractor operator ----------------------------------
// Here, we will store the result
std::string subString1{}, subString2{}, subString3{}, subString4{};
// Put the line into an istringstream for easier extraction
std::istringstream iss1(line);
iss1 >> subString1 >> subString2 >> subString3 >> subString4;
// Show result
std::cout << "\nSolution 1: Use inserter operator\n- Data: -\n" << subString1 << "\n"
<< subString2 << "\n" << subString3 << "\n" << subString4 << "\n";
// Solution 2: Use istream_iterator ----------------------------------
std::istringstream iss2(line);
std::vector token(std::istream_iterator<std::string>(iss2), {});
// Show result
std::cout << "\nSolution 2: Use istream_iterator\n- Data: -\n";
std::copy(token.begin(), token.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
// Solution 3: Use std::sregex_token_iterator ----------------------------------
const std::regex re(" ");
std::vector<std::string> token2(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
// Show result
std::cout << "\nSolution 3: Use sregex_token_iterator\n- Data: -\n";
std::copy(token2.begin(), token2.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
Итак, теперь ответ о том, как вы могли бы прочитать ваш текст файл.
Важно создать правильные структуры данных. Затем перезапишите оператор вставки и извлечения и вставьте в него вышеуказанные функции.
Пожалуйста, посмотрите демонстрационный пример ниже. Конечно, есть много других возможных решений:
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
struct ItemAndPrice {
// Data
std::string item{};
unsigned int price{};
// Extractor
friend std::istream& operator >> (std::istream& is, ItemAndPrice& iap) {
// Read a complete line from the stream and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Read the item and price from that line and check, if that worked
if (std::istringstream iss(line); !(iss >> iap.item >> iap.price))
// There was an error, while reading item and price. Set failbit of input stream
is.setf(std::ios::failbit);
}
return is;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const ItemAndPrice& iap) {
// Simple output of our internal data
return os << iap.item << " " << iap.price;
}
};
struct MarketPrice {
// Data
std::vector<ItemAndPrice> marketPriceData{};
size_t numberOfElements() const { return marketPriceData.size(); }
unsigned int weight{};
// Extractor
friend std::istream& operator >> (std::istream& is, MarketPrice& mp) {
// Read a complete line from the stream and check, if that worked
if (std::string line{}; std::getline(is, line)) {
size_t numberOfEntries{};
// Read the number of following entries and the weigth from that line and check, if that worked
if (std::istringstream iss(line); (iss >> numberOfEntries >> mp.weight)) {
mp.marketPriceData.clear();
// Now copy the numberOfEntries next lines into our vector
std::copy_n(std::istream_iterator<ItemAndPrice>(is), numberOfEntries, std::back_inserter(mp.marketPriceData));
}
else {
// There was an error, while reading number of following entries and the weigth. Set failbit of input stream
is.setf(std::ios::failbit);
}
}
return is;
};
// Inserter
friend std::ostream& operator << (std::ostream& os, const MarketPrice& mp) {
// Simple output of our internal data
os << "\nNumber of Elements: " << mp.numberOfElements() << " Weight: " << mp.weight << "\n";
// Now copy all marekt price data to output stream
if (os) std::copy(mp.marketPriceData.begin(), mp.marketPriceData.end(), std::ostream_iterator<ItemAndPrice>(os, "\n"));
return os;
}
};
// For this example I do not use argv and argc and file streams.
// This, because on Stackoverflow, I do not have files on Stackoverflow
// So, I put the file data in an istringstream. But for the below example,
// there is no difference between a file stream or a string stream
std::istringstream sourceFile{R"(2 300
abc12 130
bcd22 456
3 400
abfg12 230
bcpd22 46
abfrg2 13)"};
int main() {
// Here we will store all the resulting data
// So, read the complete source file, parse the data and store result in vector
std::vector mp(std::istream_iterator<MarketPrice>(sourceFile), {});
// Now, all data are in mp. You may work with that now
// Show result on display
std::copy(mp.begin(), mp.end(), std::ostream_iterator<MarketPrice>(std::cout, "\n"));
return 0;
}