#include "stdafx.h" #include "CsvReader.h" #include #include #ifndef Assert #include #define Assert assert #define LogToFile (void)(0); #endif namespace { /// ÆĽ̿ë state ¿­°Å°ª enum ParseState { STATE_NORMAL = 0, ///< ÀÏ¹Ý »óÅ STATE_QUOTE ///< µû¿ÈÇ¥ µÚÀÇ »óÅ }; /// ¹®ÀÚ¿­ Á¿ìÀÇ °ø¹éÀ» Á¦°ÅÇؼ­ ¹ÝȯÇÑ´Ù. std::string Trim(std::string str) { str = str.erase(str.find_last_not_of(" \t\r\n") + 1); str = str.erase(0, str.find_first_not_of(" \t\r\n")); return str; } /// \brief ÁÖ¾îÁø ¹®Àå¿¡ ÀÖ´Â ¾ËÆĺªÀ» ¸ðµÎ ¼Ò¹®ÀÚ·Î ¹Ù²Û´Ù. std::string Lower(std::string original) { std::transform(original.begin(), original.end(), original.begin(), tolower); return original; } } //////////////////////////////////////////////////////////////////////////////// /// \brief ¼¿À» ¾×¼¼½ºÇÒ ¶§, ¼ýÀÚ ´ë½Å »ç¿ëÇÒ À̸§À» µî·ÏÇÑ´Ù. /// \param name ¼¿ À̸§ /// \param index ¼¿ À妽º //////////////////////////////////////////////////////////////////////////////// void cCsvAlias::AddAlias(const char* name, size_t index) { std::string converted(Lower(name)); Assert(m_Name2Index.find(converted) == m_Name2Index.end()); Assert(m_Index2Name.find(index) == m_Index2Name.end()); m_Name2Index.insert(NAME2INDEX_MAP::value_type(converted, index)); m_Index2Name.insert(INDEX2NAME_MAP::value_type(index, name)); } //////////////////////////////////////////////////////////////////////////////// /// \brief ¸ðµç µ¥ÀÌÅ͸¦ »èÁ¦ÇÑ´Ù. //////////////////////////////////////////////////////////////////////////////// void cCsvAlias::Destroy() { m_Name2Index.clear(); m_Index2Name.clear(); } //////////////////////////////////////////////////////////////////////////////// /// \brief ¼ýÀÚ À妽º¸¦ À̸§À¸·Î º¯È¯ÇÑ´Ù. /// \param index ¼ýÀÚ À妽º /// \return const char* À̸§ //////////////////////////////////////////////////////////////////////////////// const char* cCsvAlias::operator [] (size_t index) const { INDEX2NAME_MAP::const_iterator itr(m_Index2Name.find(index)); if (itr == m_Index2Name.end()) { LogToFile(NULL, "cannot find suitable conversion for %d", index); Assert(false && "cannot find suitable conversion"); return NULL; } return itr->second.c_str(); } //////////////////////////////////////////////////////////////////////////////// /// \brief À̸§À» ¼ýÀÚ À妽º·Î º¯È¯ÇÑ´Ù. /// \param name À̸§ /// \return size_t ¼ýÀÚ À妽º //////////////////////////////////////////////////////////////////////////////// size_t cCsvAlias::operator [] (const char* name) const { NAME2INDEX_MAP::const_iterator itr(m_Name2Index.find(Lower(name))); if (itr == m_Name2Index.end()) { LogToFile(NULL, "cannot find suitable conversion for %s", name); Assert(false && "cannot find suitable conversion"); return 0; } return itr->second; } //////////////////////////////////////////////////////////////////////////////// /// \brief ÁöÁ¤µÈ À̸§ÀÇ CSV ÆÄÀÏÀ» ·ÎµåÇÑ´Ù. /// \param fileName CSV ÆÄÀÏ À̸§ /// \param seperator ÇÊµå ºÐ¸®ÀÚ·Î »ç¿ëÇÒ ±ÛÀÚ. ±âº»°ªÀº ','ÀÌ´Ù. /// \param quote µû¿ÈÇ¥·Î »ç¿ëÇÒ ±ÛÀÚ. ±âº»°ªÀº '"'ÀÌ´Ù. /// \return bool ¹«»çÈ÷ ·ÎµåÇß´Ù¸é true, ¾Æ´Ï¶ó¸é false //////////////////////////////////////////////////////////////////////////////// bool cCsvFile::Load(const char* fileName, const char seperator, const char quote) { Assert(seperator != quote); std::ifstream file(fileName, std::ios::in); if (!file) return false; Destroy(); // ±âÁ¸ÀÇ µ¥ÀÌÅ͸¦ »èÁ¦ cCsvRow* row = NULL; ParseState state = STATE_NORMAL; std::string token = ""; char buf[2048+1] = {0,}; while (file.good()) { file.getline(buf, 2048); buf[sizeof(buf)-1] = 0; std::string line(Trim(buf)); if (line.empty() || (state == STATE_NORMAL && line[0] == '#')) continue; std::string text = std::string(line) + " "; // ÆÄ½Ì lookahead ¶§¹®¿¡ ºÙ¿©ÁØ´Ù. size_t cur = 0; while (cur < text.size()) { // ÇöÀç ¸ðµå°¡ QUOTE ¸ðµåÀÏ ¶§, if (state == STATE_QUOTE) { // '"' ¹®ÀÚÀÇ Á¾·ù´Â µÎ °¡ÁöÀÌ´Ù. // 1. ¼¿ ³»ºÎ¿¡ Ư¼ö ¹®ÀÚ°¡ ÀÖÀ» °æ¿ì À̸¦ ¾Ë¸®´Â ¼¿ Á¿ìÀÇ °Í // 2. ¼¿ ³»ºÎÀÇ '"' ¹®ÀÚ°¡ '"' 2°³·Î ġȯµÈ °Í // ÀÌ Áß Ã¹¹ø° °æ¿ìÀÇ ÁÂÃø¿¡ ÀÖ´Â °ÍÀº CSV ÆÄÀÏÀÌ Á¤»óÀûÀ̶ó¸é, // ¹«Á¶°Ç STATE_NORMAL¿¡ °É¸®°Ô µÇ¾îÀÖ´Ù. // ±×·¯¹Ç·Î ¿©±â¼­ °É¸®´Â °ÍÀº 1¹øÀÇ ¿ìÃø °æ¿ì³ª, 2¹ø °æ¿ì »ÓÀÌ´Ù. // 2¹øÀÇ °æ¿ì¿¡´Â ¹«Á¶°Ç '"' ¹®ÀÚ°¡ 2°³¾¿ ³ªÅ¸³­´Ù. ÇÏÁö¸¸ 1¹øÀÇ // ¿ìÃø °æ¿ì¿¡´Â ¾Æ´Ï´Ù. À̸¦ ¹ÙÅÁÀ¸·Î Çؼ­ Äڵ带 Â¥¸é... if (text[cur] == quote) { // ´ÙÀ½ ¹®ÀÚ°¡ '"' ¹®ÀÚ¶ó¸é, Áï ¿¬¼ÓµÈ '"' ¹®ÀÚ¶ó¸é // ÀÌ´Â ¼¿ ³»ºÎÀÇ '"' ¹®ÀÚ°¡ ġȯµÈ °ÍÀÌ´Ù. if (text[cur+1] == quote) { token += quote; ++cur; } // ´ÙÀ½ ¹®ÀÚ°¡ '"' ¹®ÀÚ°¡ ¾Æ´Ï¶ó¸é // ÇöÀçÀÇ '"'¹®ÀÚ´Â ¼¿ÀÇ ³¡À» ¾Ë¸®´Â ¹®ÀÚ¶ó°í ÇÒ ¼ö ÀÖ´Ù. else { state = STATE_NORMAL; } } else { token += text[cur]; } } // ÇöÀç ¸ðµå°¡ NORMAL ¸ðµåÀÏ ¶§, else if (state == STATE_NORMAL) { if (row == NULL) row = new cCsvRow(); // ',' ¹®ÀÚ¸¦ ¸¸³µ´Ù¸é ¼¿ÀÇ ³¡ÀÇ ÀǹÌÇÑ´Ù. // ÅäÅ«À¸·Î¼­ ¼¿ ¸®½ºÆ®¿¡´Ù°¡ Áý¾î³Ö°í, ÅäÅ«À» ÃʱâÈ­ÇÑ´Ù. if (text[cur] == seperator) { row->push_back(token); token.clear(); } // '"' ¹®ÀÚ¸¦ ¸¸³µ´Ù¸é, QUOTE ¸ðµå·Î ÀüȯÇÑ´Ù. else if (text[cur] == quote) { state = STATE_QUOTE; } // ´Ù¸¥ ÀÏ¹Ý ¹®ÀÚ¶ó¸é ÇöÀç ÅäÅ«¿¡´Ù°¡ µ¡ºÙÀδÙ. else { token += text[cur]; } } ++cur; } // ¸¶Áö¸· ¼¿Àº ³¡¿¡ ',' ¹®ÀÚ°¡ ¾ø±â ¶§¹®¿¡ ¿©±â¼­ Ãß°¡ÇØÁà¾ßÇÑ´Ù. // ´Ü, óÀ½¿¡ ÆÄ½Ì lookahead ¶§¹®¿¡ ºÙÀÎ ½ºÆäÀ̽º ¹®ÀÚ µÎ °³¸¦ ¶¾´Ù. if (state == STATE_NORMAL) { Assert(row != NULL); row->push_back(token.substr(0, token.size()-2)); m_Rows.push_back(row); token.clear(); row = NULL; } else { token = token.substr(0, token.size()-2) + "\r\n"; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// \brief °¡Áö°í ÀÖ´Â ³»¿ëÀ» CSV ÆÄÀÏ¿¡´Ù ÀúÀåÇÑ´Ù. /// \param fileName CSV ÆÄÀÏ À̸§ /// \param append trueÀÏ °æ¿ì, ±âÁ¸ÀÇ ÆÄÀÏ¿¡´Ù µ¡ºÙÀδÙ. falseÀÎ °æ¿ì¿¡´Â /// ±âÁ¸ÀÇ ÆÄÀÏ ³»¿ëÀ» »èÁ¦ÇÏ°í, »õ·Î ¾´´Ù. /// \param seperator ÇÊµå ºÐ¸®ÀÚ·Î »ç¿ëÇÒ ±ÛÀÚ. ±âº»°ªÀº ','ÀÌ´Ù. /// \param quote µû¿ÈÇ¥·Î »ç¿ëÇÒ ±ÛÀÚ. ±âº»°ªÀº '"'ÀÌ´Ù. /// \return bool ¹«»çÈ÷ ÀúÀåÇß´Ù¸é true, ¿¡·¯°¡ »ý±ä °æ¿ì¿¡´Â false //////////////////////////////////////////////////////////////////////////////// bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quote) const { Assert(seperator != quote); // Ãâ·Â ¸ðµå¿¡ µû¶ó ÆÄÀÏÀ» Àû´çÇÑ Ç÷¡±×·Î »ý¼ºÇÑ´Ù. std::ofstream file; if (append) { file.open(fileName, std::ios::out | std::ios::app); } else { file.open(fileName, std::ios::out | std::ios::trunc); } // ÆÄÀÏÀ» ¿­Áö ¸øÇß´Ù¸é, false¸¦ ¸®ÅÏÇÑ´Ù. if (!file) return false; char special_chars[5] = { seperator, quote, '\r', '\n', 0 }; char quote_escape_string[3] = { quote, quote, 0 }; // ¸ðµç ÇàÀ» Ⱦ´ÜÇϸ鼭... for (size_t i=0; isize(); } //////////////////////////////////////////////////////////////////////////////// /// \brief À妽º¸¦ ÀÌ¿ëÇØ int ÇüÀ¸·Î ¼¿ °ªÀ» ¹ÝȯÇÑ´Ù. /// \param index ¼¿ À妽º /// \return int ¼¿ °ª //////////////////////////////////////////////////////////////////////////////// int cCsvTable::AsInt(size_t index) const { const cCsvRow* const row = CurRow(); Assert(row); Assert(index < row->size()); return row->AsInt(index); } //////////////////////////////////////////////////////////////////////////////// /// \brief À妽º¸¦ ÀÌ¿ëÇØ double ÇüÀ¸·Î ¼¿ °ªÀ» ¹ÝȯÇÑ´Ù. /// \param index ¼¿ À妽º /// \return double ¼¿ °ª //////////////////////////////////////////////////////////////////////////////// double cCsvTable::AsDouble(size_t index) const { const cCsvRow* const row = CurRow(); Assert(row); Assert(index < row->size()); return row->AsDouble(index); } //////////////////////////////////////////////////////////////////////////////// /// \brief À妽º¸¦ ÀÌ¿ëÇØ std::string ÇüÀ¸·Î ¼¿ °ªÀ» ¹ÝȯÇÑ´Ù. /// \param index ¼¿ À妽º /// \return const char* ¼¿ °ª //////////////////////////////////////////////////////////////////////////////// const char* cCsvTable::AsStringByIndex(size_t index) const { const cCsvRow* const row = CurRow(); Assert(row); Assert(index < row->size()); return row->AsString(index); } //////////////////////////////////////////////////////////////////////////////// /// \brief alias¸¦ Æ÷ÇÔÇØ ¸ðµç µ¥ÀÌÅ͸¦ »èÁ¦ÇÑ´Ù. //////////////////////////////////////////////////////////////////////////////// void cCsvTable::Destroy() { m_File.Destroy(); m_Alias.Destroy(); m_CurRow = -1; } //////////////////////////////////////////////////////////////////////////////// /// \brief ÇöÀç ÇàÀ» ¹ÝȯÇÑ´Ù. /// \return const cCsvRow* ¾×¼¼½º°¡ °¡´ÉÇÑ ÇöÀç ÇàÀÌ Á¸ÀçÇÏ´Â °æ¿ì¿¡´Â ±× ÇàÀÇ /// Æ÷ÀÎÅ͸¦ ¹ÝȯÇÏ°í, ´õ ÀÌ»ó ¾×¼¼½º °¡´ÉÇÑ ÇàÀÌ ¾ø´Â °æ¿ì¿¡´Â NULLÀ» /// ¹ÝȯÇÑ´Ù. //////////////////////////////////////////////////////////////////////////////// const cCsvRow* const cCsvTable::CurRow() const { if (m_CurRow < 0) { Assert(false && "call Next() first!"); return NULL; } else if (m_CurRow >= (int)m_File.GetRowCount()) { Assert(false && "no more rows!"); return NULL; } return m_File[m_CurRow]; }