Как создать функцию для чтения из текстового файла на C ++ и игнорирования определенного символа и сохранения значений данных в соответствующих переменных? - PullRequest
0 голосов
/ 28 мая 2020

У меня class date определяется следующим образом. Я запрограммировал функцию write(ostream &o) для записи даты в этом формате 27/May/2020, чтобы пользователь мог легко ее прочитать. Теперь я хочу прочитать из файла, содержащего дату в вышеупомянутом формате. Итак, как мне написать функцию на C ++, которая игнорировала бы символ '/' и сохраняла бы значение 27, например, в int day, значение "May" в string month и значение 2010 в int year . Также как я могу создать функцию, которая игнорирует определенный символ и может сохранять значения в различных типах данных в C ++?

class date{
    private :
        int day;
        string month;
        int year;

        public:
            date()
            {
                day=0;
                month="NULL";
                year=2020;
            }

            void getdate()
            {
                cout<<"Enter Date : ";
                cin>>day>>month;
            }

            void write(ostream &o) const  // to write text file or screen
            {
                o<<day<<'/'<<month.c_str()<<'/'<<year;
            }

};

1 Ответ

1 голос
/ 28 мая 2020

Вы можете использовать очень простой подход, используя свойство операторов извлечения / вставки потока, возвращающего ссылку на поток. С этим вы можете связать оператор извлечения, как в вашем примере 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, а затем разделите эту строку.

Я также покажу вам пример кода для этого.

См. некоторые общие шаблоны для разделения строки:

Разделение строки на токены - очень старая задача. Доступно множество различных решений. Все они имеют разные свойства. Некоторые из них трудно понять, некоторые сложно разработать, некоторые более сложные, медленные или быстрые или более гибкий или нет.

Альтернативы

  1. Ручная работа, множество вариантов, с использованием указателей или итераторов, может быть сложно разработать и подвержено ошибкам.
  2. Использование старого стиля std::strtok функция. Может быть небезопасной. Может быть, больше не следует использовать
  3. std::getline. Наиболее часто используемая реализация. Но на самом деле это «неправильное использование» и не очень гибкое
  4. Использование специальной современной функции, специально разработанной для с этой целью наиболее гибко и хорошо вписывается в среду 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;
}
...