#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <fstream>
#include <cassert>
using namespace std;
int id_counter = 0;
struct Song; // 전방 선언
struct Artist
{
string name;
list<Song*> songs; // 이제 Song*를 참조할 수 있음
Artist() {}
Artist(string name) : name(name) {}
};
struct Song
{
int index;
string title, album, mv_url;
Artist* artist;
Song() {}
Song(string ti, Artist* art, string alb, string mv)
: title(ti), artist(art), album(alb), mv_url(mv)
{
index = id_counter++;
}
};
list<Artist*> artist_directory[256];
const int SONG_DIRECTORY_SIZE = 10;
list<Song*> song_directory[SONG_DIRECTORY_SIZE];
const string datafilename = "songs.csv";
// 아티스트 찾기 함수
Artist* find_artist(string name)
{
list<Artist*> artist_list = artist_directory[(unsigned char)name[0]];
for (auto it = artist_list.begin(); it != artist_list.end(); it++)
{
if ((*it)->name == name)
return *it;
}
return nullptr;
}
// 노래 찾기 함수 (제목으로 찾기)
Song* find_song_by_title(string title)
{
for (int i = 0; i < SONG_DIRECTORY_SIZE; i++)
{
list<Song*>& song_list = song_directory[i];
for (auto song : song_list)
{
if (song->title == title)
return song;
}
}
return nullptr;
}
// 노래 찾기 함수 (인덱스로 찾기)
Song* find_song_by_index(int index)
{
list<Song*>& song_list = song_directory[index % SONG_DIRECTORY_SIZE];
for (auto song : song_list)
{
if (song->index == index)
return song;
}
return nullptr;
}
// 아티스트 출력 함수
void print_artist(Artist* p)
{
cout << p->name << ":" << endl;
for (auto s : p->songs)
{
cout << " " << s->index << ": " << s->title << ", " << s->album
<< ", " << s->mv_url << endl;
}
}
// 아티스트 디렉토리 출력 함수
void print_artist_directory()
{
for (int i = 0; i < 256; i++)
{
list<Artist*>& artist_list = artist_directory[i];
for (auto ptr : artist_list)
{
print_artist(ptr);
}
}
}
// 아티스트 추가 함수
Artist* add_artist(string name)
{
Artist* ptr_artist = new Artist(name);
list<Artist*>& artist_list = artist_directory[(unsigned char)name[0]];
artist_list.push_back(ptr_artist);
return ptr_artist;
}
// 노래 추가 함수
void add_song(string title, string artist, string album = "", string mv_url = "")
{
Artist* artist_ptr = find_artist(artist);
if (artist_ptr == nullptr)
{
artist_ptr = add_artist(artist);
}
Song* song_ptr = new Song(title, artist_ptr, album, mv_url);
artist_ptr->songs.push_back(song_ptr);
list<Song*>& song_list = song_directory[song_ptr->index % SONG_DIRECTORY_SIZE];
song_list.push_back(song_ptr);
}
// 노래 삭제 함수
void remove_song(string title)
{
Song* song_ptr = find_song_by_title(title);
if (song_ptr != nullptr)
{
song_ptr->artist->songs.remove(song_ptr);
song_directory[song_ptr->index % SONG_DIRECTORY_SIZE].remove(song_ptr);
delete song_ptr;
cout << "노래 '" << title << "'가 삭제되었습니다." << endl;
}
else
{
cout << "노래를 찾을 수 없습니다." << endl;
}
}
// 아티스트 삭제 함수
void remove_artist(string name)
{
Artist* artist_ptr = find_artist(name);
if (artist_ptr != nullptr)
{
for (auto song : artist_ptr->songs)
{
song_directory[song->index % SONG_DIRECTORY_SIZE].remove(song);
delete song;
}
artist_directory[(unsigned char)name[0]].remove(artist_ptr);
delete artist_ptr;
cout << "아티스트 '" << name << "'가 삭제되었습니다." << endl;
}
else
{
cout << "아티스트를 찾을 수 없습니다." << endl;
}
}
// 아티스트 디렉토리 초기화 함수
void clear_artist_directory()
{
for (int i = 0; i < 256; i++)
{
list<Artist*>& artist_list = artist_directory[i];
for (auto artist : artist_list)
{
for (auto song : artist->songs)
{
delete song;
}
delete artist;
}
artist_list.clear();
}
cout << "아티스트 디렉토리가 초기화되었습니다." << endl;
}
// split_line 함수 추가
vector<string> split_line(const string& str, char delimiter)
{
vector<string> tokens;
string token;
for (char c : str)
{
if (c == delimiter)
{
tokens.push_back(token);
token.clear();
}
else
{
token += c;
}
}
tokens.push_back(token);
return tokens;
}
// CSV 파일로부터 노래 불러오기
void load_songs(string filename)
{
string line;
ifstream songfile(filename);
while (getline(songfile, line))
{
vector<string> tokens = split_line(line, ',');
assert(tokens.size() == 4 || tokens.size() == 3);
if (tokens.size() == 4)
add_song(tokens[0], tokens[1], tokens[2], tokens[3]);
else
add_song(tokens[0], tokens[1], tokens[2]);
}
songfile.close();
}
// 노래 디렉토리 출력 함수
void print_song_directory()
{
for (int i = 0; i < SONG_DIRECTORY_SIZE; i++)
{
list<Song*>& song_list = song_directory[i];
for (auto s : song_list)
{
cout << " " << s->index << ": " << s->title << ", "
<< s->artist->name << ", " << s->album << " , " << s->mv_url << endl;
}
}
}
int main()
{
load_songs(datafilename);
print_artist_directory();
print_song_directory();
// 테스트: 노래 추가, 삭제, 찾기
add_song("New Song", "New Artist", "New Album", "http://example.com");
print_song_directory();
remove_song("New Song");
print_song_directory();
return 0;
}
코드 설명
- 글로벌 변수
- int id_counter = 0;: 각 노래에 고유한 인덱스 번호를 부여하기 위한 카운터입니다.
- list<Artist*> artist_directory[256];: 아티스트의 이름의 첫 글자를 기준으로 아티스트를 저장하는 디렉터리입니다. 아티스트 이름의 첫 글자의 ASCII 코드값을 배열 인덱스로 사용합니다.
- const int SONG_DIRECTORY_SIZE = 10;: 노래 디렉터리 크기를 10으로 설정합니다.
- list<Song*> song_directory[SONG_DIRECTORY_SIZE];: 노래를 저장하는 배열입니다. 각 노래는 고유한 인덱스 값을 기준으로 해시하여 저장됩니다.
- const string datafilename = "songs.csv";: 프로그램이 불러올 데이터 파일의 이름을 정의합니다.
- 구조체 (Structs)
- struct Artist: 아티스트를 나타내는 구조체입니다. 아티스트의 이름과 그 아티스트가 만든 노래들의 목록을 가지고 있습니다.
- string name: 아티스트의 이름.
- list<Song*> songs: 해당 아티스트의 노래 목록.
- struct Song: 노래를 나타내는 구조체입니다. 제목, 앨범, 뮤직비디오 URL, 그리고 해당 노래의 아티스트 포인터를 가지고 있습니다.
- int index: 노래의 고유한 인덱스.
- string title: 노래 제목.
- string album: 앨범 이름.
- string mv_url: 뮤직비디오 URL.
- Artist* artist: 이 노래의 아티스트에 대한 포인터.
- 함수 설명
- find_artist(string name): 아티스트 디렉토리에서 이름이 name인 아티스트를 찾습니다. 이름의 첫 글자를 기준으로 해당하는 리스트에서 검색합니다. 찾으면 해당 아티스트 포인터를 반환하고, 없으면 nullptr을 반환합니다.
- add_artist(string name): 새로운 아티스트를 추가합니다. 이름을 받아 Artist 객체를 생성하고, 해당 아티스트를 디렉토리의 올바른 위치에 삽입합니다.
- add_song(string title, string artist, string album = "", string mv_url = ""): 새로운 노래를 추가합니다. 만약 해당 아티스트가 없으면 아티스트를 추가하고, 노래를 생성하여 아티스트의 노래 목록에 추가합니다. 또한 노래를 song_directory 배열에 인덱스를 이용해 저장합니다.
- remove_song(string title): 제목이 title인 노래를 찾아 삭제합니다. 해당 노래는 아티스트의 노래 목록에서 삭제되고, song_directory에서도 삭제됩니다.
- remove_artist(string name): 이름이 name인 아티스트와 그 아티스트의 모든 노래를 삭제합니다. 아티스트의 모든 노래는 song_directory에서도 삭제됩니다.
- *print_artist(Artist p)**: 특정 아티스트의 이름과 해당 아티스트의 모든 노래를 출력합니다.
- print_artist_directory(): 모든 아티스트와 그들의 노래를 출력합니다.
- print_song_directory(): song_directory의 모든 노래를 출력합니다.
- find_song_by_title(string title): 제목이 title인 노래를 song_directory에서 찾아 반환합니다. 없으면 nullptr을 반환합니다.
- find_song_by_index(int index): 인덱스를 이용해 song_directory에서 노래를 찾아 반환합니다. 인덱스는 노래를 저장할 때 해싱에 사용된 값으로 찾아냅니다.
- split_line(const string& str, char delimiter): 주어진 문자열을 구분자(delimiter)로 분할하여 각 부분을 벡터로 반환합니다. 파일에서 데이터를 읽어올 때 사용됩니다.
- load_songs(string filename): filename 파일에서 노래 데이터를 읽어옵니다. CSV 파일에서 데이터를 읽고, 각 줄을 파싱하여 노래를 추가합니다.
- clear_artist_directory(): 모든 아티스트와 그들의 노래를 삭제하고 메모리를 정리합니다.
- 메인 함수
- load_songs(datafilename): CSV 파일로부터 노래를 불러옵니다.
- print_artist_directory(): 아티스트 디렉토리를 출력합니다.
- print_song_directory(): 노래 디렉토리를 출력합니다.
- 테스트로 새로운 노래를 추가한 뒤 출력하고, 삭제 후 다시 출력하는 과정을 보여줍니다.
주요 로직
- 데이터 저장 및 검색: 아티스트와 노래를 각각 artist_directory와 song_directory에 저장합니다. artist_directory는 아티스트 이름의 첫 글자의 ASCII 코드에 기반한 배열로 아티스트를 관리하고, song_directory는 노래의 인덱스를 해싱하여 저장합니다.
- CSV 파일 처리: load_songs() 함수는 CSV 파일에서 데이터를 읽고, 이를 기반으로 아티스트와 노래를 추가합니다.
- 추가 및 삭제: 새로운 아티스트나 노래를 추가할 때, 없으면 자동으로 아티스트를 생성하고, 삭제 시에는 해당 노래와 아티스트의 모든 노래를 동시에 삭제할 수 있습니다.
개선 사항
- 메모리 관리: 동적으로 생성한 객체들을 삭제할 수 있도록 remove_artist, remove_song 함수에서 메모리 해제를 신경 씀.
- 검색 최적화: 아티스트와 노래를 찾는 방법은 배열을 순차 탐색하는 방식으로 최적화할 여지가 있습니다. 이를 해시맵이나 트리 구조로 개선할 수 있습니다.