diff --git a/tps/fonte/musica.cpp b/tps/fonte/musica.cpp new file mode 100644 index 0000000..82cd9c0 --- /dev/null +++ b/tps/fonte/musica.cpp @@ -0,0 +1,226 @@ +#include +#include +#include +#include + +#define MAX_LINE_SIZE 860 + +typedef struct { + char id[30]; + char name[200]; + char key[15]; + char artists[40][100]; + int num_artists; + char release_date[12]; + double acousticness; + double danceability; + double energy; + int duration_ms; + double instrumentalness; + double valence; + int popularity; + float tempo; + double liveness; + double loudness; + double speechiness; + int year; +} Musica; + +Musica clone_music(Musica *music) { + return *music; +} + +char *remove_line_break(char line[]) { + int size = strlen(line); + + if (line[size - 2] == '\r') + line[size - 2] = '\0'; + + else if (line[size - 1] == '\r' || line[size - 1] == '\n') + line[size - 1] = '\0'; + + return line; +} + +char *read_quotted_csv_field(char *field_ptr, char *output) { + field_ptr++; + while (*field_ptr != '\0') { + if (*field_ptr == '"') { + if (*(field_ptr + 1) == '"') { + *output++ = '"'; + field_ptr += 2; + } else { + field_ptr++; + break; + } + } else { + *output++ = *field_ptr++; + } + } + *output = '\0'; + return field_ptr; +} + +/** + * @brief Lê uma única célula de um CSV. + * + * @param field_ptr Ponteiro para o primeiro caractere dessa célula. + * @param output Arranjo que receberá o conteúdo da célula. + * + * @return Ponteiro que aponta para uma posição depois da célula. + * Geralmente um ponteiro para uma vírgula ou o fim da linha. + */ +char *read_csv_field(char *field_ptr, char *output) { + if (*field_ptr == '"') return read_quotted_csv_field(field_ptr, output); + + while (*field_ptr != '\0' && *field_ptr != ',') *output++ = *field_ptr++; + + *output = '\0'; + return field_ptr; +} + +char *read_artist(char *output, char *artist_ptr) { + char delimiter = *artist_ptr++; + + while (*artist_ptr != delimiter) { + if (*artist_ptr == '\\') *output++ = *artist_ptr++; + *output++ = *artist_ptr++; + } + + *output = '\0'; + return artist_ptr + 1; +} + +/** + * @brief Lê o campo artists de uma música e preenche o arranjo de artistas dela. + * + * @param music Ponteiro para a struct da música. + * @param artists_field Ponteiro para o primeiro caractere do campo artists. + * Um colchete '[' em todos os casos. + */ +void read_artists(Musica *music, char *artists_field) { + artists_field++; + int i = 0; + while (1) { + artists_field = read_artist(music->artists[i++], artists_field); + if (*artists_field == ']') break; + artists_field += 2; + } + music->num_artists = i; +} + +void read_release_date(Musica *music, char field[]) { + strcpy(music->release_date, field); + if (strlen(music->release_date) == 4) strcat(music->release_date, "/01/01"); + music->release_date[4] = '/'; + music->release_date[7] = '/'; +} + +double handle_percentage(double value) { + return ceil(value) == value ? value / 100 : value; +} + +/** + * @brief Lê uma linha vinda do CSV e preenche os campos da Música. + * + * @param music Ponteiro para a struct da música. + * @param line Linha do CSV. + * + * Ex.: read_music(&music, "0.598,2018,0.136,\"['Royce Da 5\\'9""', 'Eminem', 'King Green']\",0.706,283077,0.745,1,6LZe8JfVaqcpq8yjkHtWQe,0.0,10,0.268,-5.97,0,Caterpillar (feat. Eminem & King Green),61,2018-05-04,0.441,91.08") + */ +void read_music(Musica *music, char line[]) { + char field[MAX_LINE_SIZE]; + remove_line_break(line); + + line = read_csv_field(line, field); + music->valence = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); + music->year = atoi(field); + + line = read_csv_field(line + 1, field); + music->acousticness = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); + read_artists(music, field); + + line = read_csv_field(line + 1, field); + music->danceability = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); + music->duration_ms = atoi(field); + + line = read_csv_field(line + 1, field); + music->energy = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); // Skip 'explicit' column + + line = read_csv_field(line + 1, field); + strcpy(music->id, field); + + line = read_csv_field(line + 1, field); + music->instrumentalness = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); + strcpy(music->key, field); + + line = read_csv_field(line + 1, field); + music->liveness = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); + music->loudness = atof(field); + + line = read_csv_field(line + 1, field); // Skip 'mode' column + + line = read_csv_field(line + 1, field); + strcpy(music->name, field); + + line = read_csv_field(line + 1, field); + music->popularity = atoi(field); + + line = read_csv_field(line + 1, field); + read_release_date(music, field); + + line = read_csv_field(line + 1, field); + music->speechiness = handle_percentage(atof(field)); + + line = read_csv_field(line + 1, field); + music->tempo = atof(field); +} + +void print_artists(Musica *music) { + printf("["); + + if (music->num_artists > 0) { + printf("%s", music->artists[0]); + + for (int i = 1; i < music->num_artists; i++) + printf(", %s", music->artists[i]); + } + + printf("]"); +} + +void print_music(Musica *music) { + printf("%s ## ", music->id); + print_artists(music); + printf(" ## %s ## %c%c/%c%c/%c%c%c%c ## %G ## %G ## %G ## %G ## %G ## %G ## %G ## %d\n", + music->name, + music->release_date[8], + music->release_date[9], + music->release_date[5], + music->release_date[6], + music->release_date[0], + music->release_date[1], + music->release_date[2], + music->release_date[3], + music->acousticness, + music->danceability, + music->instrumentalness, + music->liveness, + music->loudness, + music->speechiness, + music->energy, + music->duration_ms + ); +}