Проанализируйте текстовый файл, создав экземпляр нового объекта С++
Мне нужно разобрать текстовый файл, содержащий что-то вроде:
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;