Вы можете использовать очень простой подход, используя свойство операторов извлечения / вставки потока, возвращающего ссылку на поток. С этим вы можете связать оператор извлечения, как в вашем примере cin>>day>>month;
.
С этим вы можете сделать один лайнер, как в следующем примере:
#include <iostream>
#include <string>
#include <sstream>
std::istringstream iss{"27/May/2020"};
int main() {
int day{};
std::string month{};
int year{};
char slash{};
if ((std::getline(iss >> day >> slash, month, '/') >> year) && (slash == '/')) {
std::cout << day << ' ' << month << ' ' << year << '\n';
}
else {
std::cerr << "\nError: Wrong input format\n";
}
return 0;
}
РЕДАКТИРОВАТЬ
OP запросил дополнительные объяснения для одного лайнера.
if ((std::getline(iss >> day >> slash, month, '/') >> year) && (slash == '/')) {
ОК. Как вы знаете, вложенные функции будут оцениваться изнутри. Например, если у вас есть: pow(sqrt(x+100),y+2)
, то сначала будет вычислено x + 100, затем sqrt, затем y + 2 и, наконец, pow. Хорошо, понял.
Глядя на наш лайнер, это означает, что сначала будет оцениваться iss >> day
. В открытом потоке iss (вы также можете использовать std::cin
или std::ifstream
или любой другой istream
) в начале у нас есть: «27 / May / 2020». Вышеупомянутая операция извлечения извлечет цифры 2 и 7 и преобразует их в целое число 27. Это наш день.
Теперь входной поток содержит «/ May / 2020», потому что 2 и 7 уже был извлечен.
Операция iss >> day
завершена. Перегруженный оператор экстрактора >>
всегда будет возвращать ссылку на istream
, с которым он был вызван. Это означает, что iss >> day
вернет "iss".
Прототип такого оператора обычно определяется как
std::istream& operator >> (std::istream& is, SomeClass& sc) {
// Do something with SomeClass sc . . .
return is; }
Вы можете видеть, что поток возвращается. И это дает вам возможность связать оператора экстрактора. После вычисления этого первого оператора iss >> day
остальная часть строки будет выглядеть так:
if ((std::getline(iss >> slash, month, '/') >> year) && (slash == '/'))
, потому что iss >> day
был выполнен и вернул iss. Помните, что наш поток сейчас содержит «/ May / 2020». Далее мы сделаем: iss >> slash
. То есть мы извлечем sla sh из потока и сохраним его в переменной sla sh. Оператор экстрактора снова вернет iss. И данные в потоке теперь «Май / 2020». Новый лайнер будет:
if ((std::getline(iss, month, '/') >> year) && (slash == '/'))
Это мы можем понять. std::getline will extract a string from the stream, until it sees a slash. That string is stored in the variable month. The data in the stream will now be "2020". And guest what?
std :: getline```` также вернет ссылку на данный поток. Теперь один лайнер будет
if ((iss >> year) && (slash == '/'))
Мы знаем, что теперь будет: iss >> year
будет оцениваться. «2020» будет преобразовано в целое число 2020. Теперь поток пуст. Все извлечено. Оператор >>
снова вернет iss. Давая нам:
if ((iss) && (slash == '/'))
Хм, if
ожидает логическое выражение, как это работает с iss? Можно объяснить: If ожидает логическое выражение и, что очень умно, поток как перегруженный оператор типа bool. Прочтите здесь . И этот оператор возвращается, если поток в хорошем состоянии или сбой.
Если бы какая-то операция извлечения во всей цепочке завершилась неудачно, то оператор bool вернет false. И тогда мы покажем сообщение об ошибке.
В качестве дополнительной проверки мы проверяем содержимое переменной «sla sh».
Итак, готово. Надеюсь, я смогу объяснить это понятным образом.
End EDIT
Но, может быть, это безопаснее, если вы сначала прочтете полная строка (std::string
на std::getline
, а затем разделите эту строку.
Я также покажу вам пример кода для этого.
См. некоторые общие шаблоны для разделения строки:
Разделение строки на токены - очень старая задача. Доступно множество различных решений. Все они имеют разные свойства. Некоторые из них трудно понять, некоторые сложно разработать, некоторые более сложные, медленные или быстрые или более гибкий или нет.
Альтернативы
- Ручная работа, множество вариантов, с использованием указателей или итераторов, может быть сложно разработать и подвержено ошибкам.
- Использование старого стиля
std::strtok
функция. Может быть небезопасной. Может быть, больше не следует использовать std::getline
. Наиболее часто используемая реализация. Но на самом деле это «неправильное использование» и не очень гибкое - Использование специальной современной функции, специально разработанной для с этой целью наиболее гибко и хорошо вписывается в среду STL и ландшафт алгоритмов. Но медленнее.
См. 4 примера в одном фрагменте кода.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <forward_list>
#include <deque>
using Container = std::vector<std::string>;
std::regex delimiter{ "," };
int main() {
// Some function to print the contents of an STL container
auto print = [](const auto& container) -> void { std::copy(container.begin(), container.end(),
std::ostream_iterator<std::decay<decltype(*container.begin())>::type>(std::cout, " ")); std::cout << '\n'; };
// Example 1: Handcrafted -------------------------------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Search for comma, then take the part and add to the result
for (size_t i{ 0U }, startpos{ 0U }; i <= stringToSplit.size(); ++i) {
// So, if there is a comma or the end of the string
if ((stringToSplit[i] == ',') || (i == (stringToSplit.size()))) {
// Copy substring
c.push_back(stringToSplit.substr(startpos, i - startpos));
startpos = i + 1;
}
}
print(c);
}
// Example 2: Using very old strtok function ----------------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Split string into parts in a simple for loop
#pragma warning(suppress : 4996)
for (char* token = std::strtok(const_cast<char*>(stringToSplit.data()), ","); token != nullptr; token = std::strtok(nullptr, ",")) {
c.push_back(token);
}
print(c);
}
// Example 3: Very often used std::getline with additional istringstream ------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Put string in an std::istringstream
std::istringstream iss{ stringToSplit };
// Extract string parts in simple for loop
for (std::string part{}; std::getline(iss, part, ','); c.push_back(part))
;
print(c);
}
// Example 4: Most flexible iterator solution ------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
//
// Everything done already with range constructor. No additional code needed.
//
print(c);
// Works also with other containers in the same way
std::forward_list<std::string> c2(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
print(c2);
// And works with algorithms
std::deque<std::string> c3{};
std::copy(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {}, std::back_inserter(c3));
print(c3);
}
return 0;
}