Проанализируйте текстовый файл, создав экземпляр нового объекта С++

Мне нужно разобрать текстовый файл, содержащий что-то вроде:

1|Song Title|Release date||"ignore me"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0

который является номером песни, за которым следует дата выпуска, а затем веб-сайт, который мне нужно игнорировать, а затем серия 0 и 1, которая может представлять вектор жанров.

Мне нужен способ разделить эти данные и игнорировать тот, который говорит веб-сайт, в то же время создавая новый экземпляр объекта песни, который имеет: (int songNumber, string songTitle, vector * genres, string releaseDate )

Спасибо!

Ответы

Ответ 1

С++ String Toolkit Library (StrTk) имеет следующее решение вашей проблемы:

#include <string>
#include <deque>
#include "strtk.hpp"

struct song_type
{
   unsinged int id;
   std::string release_date;
   std::string url;
   char genre[8];
};

strtk_parse_begin(song_type)
 strtk_parse_type(id)
 strtk_parse_type(release_date)
 strtk_parse_type(url)
 strtk_parse_type(genre[0])
 strtk_parse_type(genre[1])
 strtk_parse_type(genre[2])
 strtk_parse_type(genre[3])
 strtk_parse_type(genre[4])
 strtk_parse_type(genre[5])
 strtk_parse_type(genre[6])
 strtk_parse_type(genre[7])
strtk_parse_end()

int main()
{
   std::deque<song_type> song_list;

   strtk::for_each_line("songs.txt",
                        [&song_list](const std::string& line)
                        {
                           song_type s;
                           if (strtk::parse(line,"|",s))
                              song_list.push_back(s);
                        });

   return 0;
}

Дополнительные примеры можно найти Здесь

Ответ 2

  • Определите класс Song, который содержит данные в требуемой форме, как указано выше.
  • реализовать Song::operator>>(const istream&); для заполнения класса путем анализа указанных выше данных из потока ввода
  • прочитайте файл строки за строкой, используя string::getline
  • для каждой строки, конвертируйте в stringstream, а затем используйте operator>>, чтобы заполнить поля в экземпляре Song.

Простое, чтобы токенизировать строковый поток с помощью '|' символ как разделитель, который будет основной частью работы.

int main()
{
   std::string token;
   std::string line("1|Song Title|Release date||\"ignore me\"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0");

   std::istringstream iss(line);
   while ( getline(iss, token, '|') )
   {
      std::cout << token << std::endl;
   }
   return 0;
}

Код снят с здесь.

Ответ 3

Обычно вы делаете это, перегружая operator>> для типа объекта:

struct song_data { 
    std::string number;
    std::string title;
    std::string release_date;
    // ...
};

std::istream &operator>>(std::istream &is, song_data &s_d) {        
    std::getline(is, s_d.number, '|');
    std::getline(is, s_d.title, '|');
    std::getline(is, s_d.release_date, '|');
    std::string ignore;
    std::getline(is, ignore, '|');
    // ...
    return is;
}

В зависимости от того, есть ли больше полей, которые вы, возможно, захотите проигнорировать (особенно трейлинг-поля), иногда бывает удобнее читать всю строку в строке, а затем помещать ее в поток istringstream и анализировать отдельные поля. В частности, это может избежать дополнительной работы, читающей больше полей, на которые вас не волнует, вместо этого просто переходите к следующей строке, когда вы разбираете поля, которые вам интересны.

Изменить: я бы, вероятно, справился с жанрами, добавив std::vector<bool> genres; и прочитав 0 и 1 в этот вектор. Затем я добавляю перечисление, определяющее, какой жанр обозначается определенной позицией в векторе, поэтому (например) проверка того, будет ли определенная песня классифицирована как "страна", выглядит примерно так:

enum { jazz, country, hiphop, classic_rock, progressive_rock, metal /*, ... */};

if (songs[i].genres[country])

if (songs[i].genres[hiphop])
    process_hiphop(songs[i]);

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

Что касается работы с сотнями песен, обычный способ был бы (как подразумевается выше) создать что-то вроде: std::vector<song_data> songs;. Используя извлечение потока, как показано выше, вы можете скопировать данные из файла в вектор:

std::copy(std::istream_iterator<song_data>(infile),
          std::istream_iterator<song_data>(),
          std::back_inserter(songs));

Если вы, вероятно, будете искать песни в основном по имени (на одном примере), вы можете использовать std::map<std::string, song_data> songs. Это позволит сделать что-то вроде:

songs["new song"].release_date = Today;