사용자 도구

사이트 도구


kb:luacsvparser

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:luacsvparser [2014/11/06 16:57] (현재)
줄 1: 줄 1:
 +====== Lua CSV Parser ======
 +루아를 이용해서 CSV 파일을 다루기 위해 DLL을 만들었다. 루아에서 DLL 파일을 로드하는 방법은 LuaBinaryExtension 페이지를 참고하기 바란다. Visual C++ CRT 라이브러리를 정적으로 링크해 버렸더니 하는 일에 비해 크기가 꽤 커저버렸다.
 +
 +
 +====== 사용법 ======
 +==== 불러오기 ====
 +파일에서 불러올 수도 있고, 스트링을 그냥 파싱할 수도 있다.
 +
 +  * 파싱 함수 반환값은 2차원 배열이다. ​
 +  * 라인 맨 앞 글자가 '#'​인 경우 그 라인 전체를 주석으로 간주한다.
 +
 +<code lua>
 +require("​LuaCsv"​)
 +
 +data = csv.load_from_file("​test.csv"​) -- 파일에서 로드
 +print(data[1][1])
 +
 +data = csv.load_from_string("​1,​2\n3,​4"​) -- 문자열에서 로드
 +print(data[1][1])
 +</​code>​
 +
 +==== 저장하기 ====
 +저장 함수에 2차원 배열을 넘기면 된다.
 +
 +  * 문자열, 숫자, 불린 값만 저장이 가능하다.
 +  * NIL 값은 그냥 빈 문자열로 저장이 된다.
 +  ​
 +<code lua>
 +require("​LuaCsv"​)
 +
 +t = {{1,​2},​{3,​4}}
 +csv.save_to_file(t,​ "​test.csv"​) -- 파일로 저장
 +print(csv.save_to_string(t)) -- 문자열로 저장
 +</​code>​
 +
 +==== 따옴표와 분리자 설정 ====
 +<code lua>
 +require("​LuaCsv"​)
 +print(csv.seperator)
 +print(csv.quote)
 +print(csv.version)
 +</​code>​
 + 
 +====== 소스 ======
 +[[CsvFileProcessing]] 페이지에서 가져온 소스를 적당히 손 본 것이다.
 +<code cpp>
 +////////////////////////////////////////////////////////////////////////////////​
 +/// \file LuaCsv.cpp
 +/// \author excel96
 +/// \date 2007.3.7
 +////////////////////////////////////////////////////////////////////////////////​
 +
 +#include <​windows.h>​
 +#include <​fstream>​
 +#include <​sstream>​
 +#include <​vector>​
 +
 +extern "​C" ​
 +{
 +    #include <​lua.h>​
 +    #include <​lualib.h>​
 +    #include <​lauxlib.h>​
 +
 +    const char* MODULE_NAME ​      = "​csv";​
 +    const char* MODULE_VERSION ​   = "​1.0";​
 +    const char  DEFAULT_SEPERATOR = ',';​
 +    const char  DEFAULT_QUOTE ​    = '"';​
 +
 +    const char* VERSION_NAME ​     = "​version";​
 +    const char* SEPERATOR_NAME ​   = "​seperator";​
 +    const char* QUOTE_NAME ​       = "​quote";​
 +
 +    typedef std::​vector<​std::​string>​ ROW;
 +    typedef std::​vector<​ROW>​ ROWS;
 +
 +    char get_seperator(lua_State* L)
 +    {
 +        char seperator = DEFAULT_SEPERATOR;​
 +
 +        lua_getfield(L,​ LUA_GLOBALSINDEX,​ MODULE_NAME);​
 +        if (lua_type(L,​ -1) == LUA_TTABLE)
 +        {
 +            lua_getfield(L,​ -1, SEPERATOR_NAME);​
 +            if (lua_type(L,​ -1) == LUA_TNUMBER)
 +                seperator = static_cast<​char>​(lua_tonumber(L,​ -1));
 +            lua_pop(L, 1);
 +        }
 +        lua_pop(L, 1);
 +
 +        return seperator;
 +    }
 +
 +    char get_quote(lua_State* L)
 +    {
 +        char quote = DEFAULT_QUOTE;​
 +
 +        lua_getfield(L,​ LUA_GLOBALSINDEX,​ MODULE_NAME);​
 +        if (lua_type(L,​ -1) == LUA_TTABLE)
 +        {
 +            lua_getfield(L,​ -1, QUOTE_NAME);​
 +            if (lua_type(L,​ -1) == LUA_TNUMBER)
 +                quote = static_cast<​char>​(lua_tonumber(L,​ -1));
 +            lua_pop(L, 1);
 +        }
 +        lua_pop(L, 1);
 +
 +        return quote;
 +    }
 +
 +    int load(lua_State* L, std::​istream&​ stream, char seperator, char quote)
 +    {
 +        enum ParseState
 +        {
 +            STATE_NORMAL,​ // 일반 상태
 +            STATE_QUOTE ​  // 따옴표 뒤의 상태
 +        };
 +
 +        if (!stream.good())
 +            return luaL_error(L,​ "​invalid stream"​);​
 +
 +        ROWS rows;
 +
 +        ROW*        row        = NULL;
 +        ParseState ​ state      = STATE_NORMAL; ​
 +        std::string text       = ""; ​          
 +        std::string token      = ""; ​          
 +        int         ​lineNumber = 0;            ​
 +
 +        while (stream.good())
 +        {
 +            ++lineNumber;​
 +            std::​getline(stream,​ text);
 +
 +            if (text.empty() || (state == STATE_NORMAL && text[0] == '#'​)) ​
 +                continue;
 +
 +            text.append(" ​ ");
 +
 +            const char* cur = text.c_str();​
 +            const char* end = cur + text.size();​
 +            while (cur < end)
 +            {
 +                if (state == STATE_QUOTE)
 +                {
 +                    if (*cur == quote)
 +                    {
 +                        if (*(cur + 1) == quote)
 +                        {
 +                            token.append(1,​ quote);
 +                            ++cur;
 +                        }
 +                        else
 +                        {
 +                            state = STATE_NORMAL;​
 +                        }
 +                    }
 +                    else
 +                    {
 +                        token.append(1,​ *cur);
 +                    }
 +                }
 +                else if (state == STATE_NORMAL)
 +                {
 +                    if (row == NULL)
 +                    {
 +                        rows.push_back(ROW());​
 +                        row = &​(rows.back());​
 +                    }
 +
 +                    if (*cur == seperator)
 +                    {
 +                        row->​push_back(token);​
 +                        token.clear();​
 +                    }
 +                    else if (*cur == quote)
 +                    {
 +                        state = STATE_QUOTE;​
 +                    }
 +                    else
 +                    {
 +                        token.append(1,​ *cur);
 +                    }
 +                }
 +
 +                ++cur;
 +            }
 +
 +            if (state == STATE_NORMAL)
 +            {
 +                if (row)
 +                {
 +                    row->​push_back(token.erase(token.size()-2,​ 2));
 +                    token.clear();​
 +                    row = NULL;
 +                }
 +            }
 +            else
 +            {
 +                token.erase(token.size()-2,​ 2);
 +                token.append("​\r\n"​);​
 +            }
 +        }
 +
 +        lua_newtable(L);​
 +
 +        for (size_t j=0; j<​rows.size();​ ++j)
 +        {
 +            lua_pushnumber(L,​ static_cast<​int>​(j+1));​
 +            lua_newtable(L);​
 +
 +            const ROW& r = rows[j];
 +            for (size_t i=0; i<​r.size();​ ++i)
 +            {
 +                lua_pushnumber(L,​ static_cast<​int>​(i+1));​
 +                lua_pushstring(L,​ r[i].c_str());​
 +                lua_rawset(L,​ -3);
 +            }
 +
 +            lua_rawset(L,​ -3);
 +        }
 +
 +        return 1;
 +    }
 +
 +    static void save(lua_State* L, std::​ostream&​ stream, char seperator, char quote)
 +    {
 +        luaL_checktype(L,​ -1, LUA_TTABLE);​
 +
 +        ROWS rows;
 +        int rowCount = 1;
 +        int colCount = 1;
 +
 +        lua_pushnil(L);​
 +        while (lua_next(L,​ -2) != 0)
 +        {
 +            luaL_checktype(L,​ -1, LUA_TTABLE);​
 +
 +            ROW row;
 +
 +            lua_pushnil(L);​
 +            while (lua_next(L,​ -2) != 0)
 +            {
 +                switch (lua_type(L,​ -1))
 +                {
 +                case LUA_TSTRING:​
 +                    row.push_back(lua_tostring(L,​ -1));
 +                    break;
 +                case LUA_TNUMBER:​
 +                    row.push_back(lua_tostring(L,​ -1));
 +                    break;
 +                case LUA_TBOOLEAN:​
 +                    row.push_back(lua_toboolean(L,​ -1) ? "​true"​ : "​false"​);​
 +                    break;
 +                case LUA_TNIL:
 +                    row.push_back("​nil"​);​
 +                    break;
 +                default:
 +                    luaL_error(L, ​
 +                        "​cannot convert (%s) column to string (row:​%d,​col:​%d)", ​
 +                        lua_typename(L,​ -1), rows.size() + 1, row.size() + 1
 +                        );
 +                    break;
 +                }
 +
 +                lua_pop(L, 1);
 +            }
 +
 +            lua_pop(L, 1);
 +            rows.push_back(row);​
 +        }
 +
 +        char specialChars[5] = { seperator, quote, '​\r',​ '​\n',​ 0 };
 +        char quoteEscapeString[3] = { quote, quote, 0 };
 +
 +        for (size_t i=0; i<​rows.size();​ i++)
 +        {
 +            const ROW& row = rows[i];
 +
 +            std::string line;
 +
 +            for (size_t j=0; j<​row.size();​ j++)
 +            {
 +                const std::​string&​ token = row[j];
 +
 +                if (token.find_first_of(specialChars) == std::​string::​npos)
 +                {
 +                    line.append(token);​
 +                }
 +                else
 +                {
 +                    line.append(1,​ quote);
 +
 +                    for (size_t k=0; k<​token.size();​ k++)
 +                    {
 +                        if (token[k] == quote) line.append(quoteEscapeString);​
 +                        else line.append(1,​ token[k]);
 +                    }
 +
 +                    line.append(1,​ quote);
 +                }
 +
 +                if (j != row.size() - 1) { line.append(1,​ seperator); }
 +            }
 +
 +            stream << line << std::endl;
 +        }
 +    }
 +
 +    static int load_from_file(lua_State* L)
 +    {
 +        const char* fileName = luaL_checkstring(L,​ -1);
 +
 +        std::​ifstream file(fileName,​ std::​ios::​in);​
 +        if (!file)
 +            return luaL_error(L,​ "​cannot open specified file"​);​
 +
 +        char seperator = get_seperator(L);​
 +        char quote = get_quote(L);​
 +
 +        return load(L, file, seperator, quote);
 +    }
 +
 +    static int load_from_string(lua_State* L)
 +    {
 +        const char* str = luaL_checkstring(L,​ -1);
 +
 +        char seperator = get_seperator(L);​
 +        char quote = get_quote(L);​
 +
 +        std::​stringstream memory(str);​
 +        memory.imbue(std::​locale::​classic());​
 +
 +        return load(L, memory, seperator, quote);
 +    }
 +
 +    static int save_to_file(lua_State* L)
 +    {
 +        luaL_checktype(L,​ -1, LUA_TSTRING);​
 +        luaL_checktype(L,​ -2, LUA_TTABLE);​
 +
 +        std::​ofstream file(lua_tostring(L,​ -1), std::​ios::​out | std::​ios::​trunc);​
 +        if (!file)
 +            return luaL_error(L,​ "​cannot open specified file"​);​
 +
 +        lua_pop(L, 1);
 +
 +        char seperator = get_seperator(L);​
 +        char quote = get_quote(L);​
 +
 +        save(L, file, seperator, quote);
 +
 +        return 0;
 +    }
 +
 +    static int save_to_string(lua_State* L)
 +    {
 +        luaL_checktype(L,​ -2, LUA_TTABLE);​
 +
 +        std::​stringstream memory;
 +        memory.imbue(std::​locale::​classic());​
 +
 +        char seperator = get_seperator(L);​
 +        char quote = get_quote(L);​
 +
 +        save(L, memory, seperator, quote);
 +
 +        lua_pushstring(L,​ memory.str().c_str());​
 +        return 1;
 +    }
 +
 +    int __declspec(dllexport) luaopen_LuaCsv(lua_State* L)
 +    {
 +        static const luaL_Reg csvlib[] = 
 +        {
 +            { "​load_from_file", ​  ​load_from_file },
 +            { "​load_from_string",​ load_from_string },
 +            { "​save_to_file", ​    ​save_to_file },
 +            { "​save_to_string", ​  ​save_to_string },
 +            { NULL,               NULL }
 +        };
 +
 +        luaL_register(L,​ MODULE_NAME,​ csvlib);
 +
 +        lua_pushnumber(L,​ DEFAULT_SEPERATOR);​
 +        lua_setfield(L,​ -2, SEPERATOR_NAME);​
 +
 +        lua_pushnumber(L,​ DEFAULT_QUOTE);​
 +        lua_setfield(L,​ -2, QUOTE_NAME);​
 +
 +        lua_pushstring(L,​ MODULE_VERSION);​
 +        lua_setfield(L,​ -2, VERSION_NAME);​
 +
 +        return 0;
 +    }
 +}
 +
 +#ifdef _DEBUG
 +    #pragma comment(lib,​ "​lualibDebug"​)
 +#else
 +    #pragma comment(lib,​ "​lualib"​)
 +#endif
 +
 +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,​ LPVOID lpReserved)
 +{
 +    hModule = hModule;
 +    ul_reason_for_call = ul_reason_for_call;​
 +    lpReserved = lpReserved;
 +    return TRUE;
 +}
 +</​code>​
 +----
 +  * see also [[Lua]], [[LuaBinaryExtension]],​ [[CsvFileProcessing]]
  
kb/luacsvparser.txt · 마지막으로 수정됨: 2014/11/06 16:57 (바깥 편집)