MySQL UDF입니다. 함수명은 TOKEN()입니다.
사용법:
[인수의 수가 1인 경우] token("aaa bbb ccc") --> 'aaa' 리턴 token(" aaa bbb") --> 'NULL' 리턴, 왜냐하면 시작문자가 공백문자로 구분자이기 때문에
[인수의 수가 2인 경우] token("aaa bbb ccc", 2) --> 'bbb' 두번째 토큰을 리턴, 만약 0이면 무시하고 'aaa'를 리턴, 3이면 'ccc', 4이면 'NULL'를 리턴.
token("aa$cc&dd", "$&") --> 'aa'를 리턴, 즉 구분자를 기준으로 첫번째 토큰을 리턴, 만약 "aacc&dd"면 'aacc'를 리턴.
[인수의 수가 3인 경우] token("aa&cc&dd", "&$", 2) --> 'cc'를 리턴 token("aa&cc&dd", 3, "&$") --> 'dd'를 리턴, 즉 두번째 인수와 세번째 인수는 위치가 바꿔도 상관없음.
윈도우 환경에서 만들었습니다. 좀만 바꾸면 리눅스등 다른 OS에서도
돌아갈 수 있을겁니다. 조금이라도 도움이 됐으면 좋겠습니다.
저가 여기에 너무 신세진게 많아서리..^^;;
//////////////////////////////////////////////////////////////////////////////
// token.h //
//////////////////////////////////////////////////////////////////////////////
#include "mysql.h" #define TOKEN_API extern "C" __declspec(dllexport)
TOKEN_API my_bool token_init( UDF_INIT *initid, UDF_ARGS *args, char *message ); TOKEN_API void token_deinit( UDF_INIT *initid ); TOKEN_API char* token( UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char* /* error */ );
//////////////////////////////////////////////////////////////////////////////
// token.cpp //
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h" #include "token.h"
#define MAX_TOKEN 255 #define DEFAULT_SEPERATORS " \t\n\r" #define DEFAULT_SEPERATORS_LEN 4 #define DEFAULT_ORDINAL 1
struct token_argv { char* seperators; int seperators_len; int ordinal; //몇번째 토큰을 가져올까?를 지정 };
TOKEN_API my_bool token_init( UDF_INIT *initid, UDF_ARGS *args, char *message ) { enum Item_result *type = args->arg_type; int default_seperators = 0; int default_ordinal = 0;
/*구지 여기서 구조체 만들어서 메모리 할당하고 initid를 이용해 넘겨주고 넘겨받고 할 필요없이 token() 자체에서 할 수 있지만 token()가 갖은 원래 기능에 집중하기 위해 예비작업을 분리한다. */
token_argv *argv = new token_argv; if( !argv ) { strcpy( message, "Memory alloc failed!" ); return 1; } argv->seperators = 0; argv->seperators_len = 0; argv->ordinal = DEFAULT_ORDINAL; /*인수의 수와 타입 체크, 초기화*/ switch( args->arg_count ) { case 1: //공백문자를 구분자로 하고 첫번째 토큰을 원하는 경우이다. if( type[0] != STRING_RESULT ) { strcpy( message, "Wrong argument type" ); goto TOKEN_ERROR; } break;
case 2:
//사용자 지정문자를 구분자로 하고 윈하는 토큰이 첫번째가 아닐 수 있다. //두번째 인수가 문자열, 즉 구분자이면 자동으로 첫번째 토큰을 원하는 것으로 //간주한다. 반대로 두번째 인수가 정수이면 공백문자의 구분자로 지정 토콘을 //원하는 것으로 간주한다.
if( type[0] != STRING_RESULT || type[1] == REAL_RESULT ) { strcpy( message, "Wrong argument type" ); goto TOKEN_ERROR; }
if( type[1] == INT_RESULT ) default_ordinal = 1; else if ( args->lengths[1] > 0 ) default_seperators = 1; break;
case 3:
//사용자 지정문자를 구분자로 하고 윈하는 토큰이 첫번째가 아닐 수 있다. //구분자와 토큰지정의 위치가 바뀔 수 있다. 이 경우는 매우 제한적인 경우 //에 필요할 것이다.
if( type[0] != STRING_RESULT || type[1] == REAL_RESULT || type[2] == REAL_RESULT || type[1] == type[2] ) { strcpy( message, "Wrong argument type" ); goto TOKEN_ERROR; }
if( type[1] == INT_RESULT ) { default_ordinal = 1; if( args->lengths[2] > 0 ) default_seperators = 2; } else { default_ordinal = 2; if( args->lengths[1] > 0 ) default_seperators = 1; } break;
default: strcpy( message, "Wrong number of arguments" ); goto TOKEN_ERROR; }
//첫번째 토큰을 원하는게 아니라면 if( default_ordinal ) { argv->ordinal = *((int*)args->args[default_ordinal]); if( argv->ordinal < 1 ) argv->ordinal = DEFAULT_ORDINAL; }
if( default_seperators ) //공백문자 구분자를 원하는게 아니라면 { argv->seperators = new char[args->lengths[default_seperators]+1]; if( !argv->seperators ) { strcpy( message, "Memory alloc failed!" ); goto TOKEN_ERROR; } strcpy( argv->seperators, args->args[default_seperators] ); argv->seperators[args->lengths[default_seperators]] = 0; argv->seperators_len = args->lengths[default_seperators]; } else //공백문자 구분자 사용 { argv->seperators = new char[DEFAULT_SEPERATORS_LEN+1]; if( !argv->seperators ) { strcpy( message, "Memory alloc failed!" ); goto TOKEN_ERROR; } strcpy( argv->seperators, DEFAULT_SEPERATORS ); argv->seperators[DEFAULT_SEPERATORS_LEN] = 0; argv->seperators_len = DEFAULT_SEPERATORS_LEN; }
initid->ptr = (char*)argv; initid->max_length = MAX_TOKEN; return 0; TOKEN_ERROR: if( argv ) { if( argv->seperators ) delete[] argv->seperators; delete argv; } return 1; }
/*꼭 여기서 메모리를 해제해야할 필요는 없다. token()에서 사용하고 해제해도 된다. 그래도 기능분리 원칙에 따라 여기서 해제한다.*/ TOKEN_API void token_deinit( UDF_INIT *initid ) { token_argv *argv = (token_argv*)initid->ptr; if( argv ) { if( argv->seperators ) delete[] argv->seperators; delete argv; } }
/*리턴값이 255byte 이하라면 별도의 결과값을 위한 메모리 할당 필요없이 result인수를 사용한다.*/
TOKEN_API char* token( UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char* /*error */ ) { token_argv *argv = (token_argv*)initid->ptr; char *str = args->args[0]; int len = args->lengths[0];
int index = 0; int count = 0; int x1 = 0; int x2 = len;
//핵심 동작 while( index <= len ) { //주위! 종료문자는 자연적 구분자로 본다. if( index == len || strchr( argv->seperators, str[index] ) ) //이제 구분자를 찾으면 { x2 = index; count++; //토큰을 찾은 것이므로 count증가 if( count == argv->ordinal ) //찾던 위치의 토큰이면 중지 break; //다시 연속된 구분자들을 skip한다. while( index < len ) { if( !strchr( argv->seperators, str[index] ) ) //구분자가 아니면... break; index++; } x1 = index; //토큰의 시작 인덱스를 저장 } index++; }
*is_null = 1; if( x2 > x1 ) { //255byte 제한 때문에 *length = ((x2 - x1) <= MAX_TOKEN) ? (x2 - x1) : MAX_TOKEN; memcpy( result, str+x1, (size_t)*length ); *is_null = 0; //마지막 글자의 한글이 짤릴수도 있는데 여기서 그것까지 다루진 않는다. }
return result; }
|