Q&A에 문자열을 C언어에서의 문자열 표현 방식으로 바꾸고
반대로 C언어에서의 문자열 표현 방식을 실제 컴파일된 문자열로 바꾸는
그런 함수가 있는지 질문을 했더니 없다는 것이 중론인 것 같네요..
당장 필요하긴 하고 해서 그냥 만들었습니다.
함수 이름은 좀 고민스럽습니다. 일단 소스코드상의 표현을 Src라고
보고, 컴파일된 후의 문자열을 Str라고 봐서 SrcToStr, StrToSrc라고
이름은 지었는데 좋은 이름 추천좀 해 주세요.
혹시나 이런 비슷한 함수가 API나 Ansi C++에 있다면
김백일님.. 각오하십시오.. 백일님이 없다고 해서 만든거니까 삽질하게
한 대가를 톡톡히 치르셔야 할겁니다. ^^ ㅋㅋㅋ
특이점이라고 할 것 까지는 없지만 일단 디코딩 함수인 SrcToStr은
다음과 기능을 제공합니다.
1. 기본 확장 문자코드 지원
\n \t \v \b \r \a \\ \? \' \" 총 10개의 확장문자코드를 지원합니다.
2. Octal표현 지원
\040 :8진수로 스페이스죠..
이와 같은 표현을 지원합니다. 숫자의 범위를 정확히 검사하므로 안전
하게 동작합니다. \510 과 같이 256범위를 벗어나거나 \108 과 같이
8진수로 표현 불가능한 경우도 걸러냅니다.
3. Hex 표현 지원
\x2f 과 같은 16진수 표현을 지원합니다.
4. 만일 디코딩에 성공했다면 반환값은 0입니다.
만일 디코딩시에 오류가 발견된다면 반환값은 오류가 난 지점(1베이스)
입니다. 따라서 Syntex에러를 표시할 때 처럼 에러 위치를 비교적 근
접하게 표시해 줄 수 있겠죠.
5. DBCS에서 안전하게 동작합니다.
즉, 한글에 대해서도 안전하다는 얘기입니다. 단, 2byte이상의 MBCS
스트링에 대해서는 오동작의 가능성이 있습니다.
인코딩 함수인 StrToSrc는 다음과 같은 기능을 제공합니다.
1. 일반 가시문자가 아닌 것들에 대한 인코딩 - 16진수 표현을 사용합니다.
8진수 표기보다는 16진수가 보다 익숙한 것 같아서 16진수 표현으로 통
일했습니다.
2. 확장 문자코드를 지원하는 경우 16진수 표현을 쓰지 않고 확장 문자코드
표현을 사용합니다.
즉, 0x0C (\r) 의 경우 \x0c로 인코딩 하지 않고 \r로 인코딩합니다.
단, \?의 경우 일반적으로 인코딩을 하지 않기 때문에 그냥 ?로 표기합니다.
3. 반환값은 항상 0입니다. (언제나 성공할 수 있으므로)
개선해야할 사항은 다음과 같습니다.
1. 함수 이름이 정말 맘에 안듭니다. 이름을 바꾸고 싶습니다.
2. 결과값에 대해 SetLength() 함수로 길이를 조절하므로 1회의 메모리 복
사가 발생합니다. 당연히 효율이 좀 떨어지겠죠.
디코딩 루틴인 SrcToStr의 경우 큰 문제가 없지만 인코딩 루틴인
StrToSrc 함수는 심각합니다.
안전빵으로 입력값의 길이의 4배만큼 길이를 확보하고 인코딩 한 다음
최종길이 만큼 재조정하는 무식한 구조입니다. 이에 대한 개선이 필요
합니다.
3. 루프가 좀 지저분합니다.
시간이 없어서 대충 만들다보니.. 인코딩쪽하고 디코딩쪽이 루프 돌리
는게 서로 다릅니다. ㅠ ㅠ 하나는 while을 쓰고 다른 하나는 for를
씁니다.
일반적으로 while을 쓰는 것이 속도는 빠르지만 DBCS를 지원하려면
IsLeadByte()를 써야 하기 때문에 for가 더 유리해 보입니다.
한쪽에서는 IsLeadByte 호출이 많아서 for를 썼습니다.
제가 알기론 IsLeadByte는 상당히 비싼 함수로 알고 있습니다. 엄청
신경이 쓰입니다.
4. 무식한 탐색 함수들이 2개 있습니다. 스탠다드 C의 함수로도 처리 가능
한 것인데 이름을 까먹어서 찾기도 귀찮고 해서 그냥 대충 만들었습
니다.
5. 뭐, 결론은 전반적으로 코드 최적화를 해야 한다.. 였습니다.
좀 더 개선해 주실 수 있는 분은 직접 수정하신 후 다시 올려주시면 고맙
겠습니다.
덤으로 HexEncode라는 함수와 HexDecode라는 함수가 같이 있습니다.
이건 VCL의 HexToBin, BinToHex 함수와 동일한 기능을 하지만 대소문자를
선택할 수 있습니다. VCL의 경우 소문자만 지원하죠.
사용예제1:
String sSrc = "헤헤헤\r\nTest임다";
String sDest;
StrToSrc(sDest,sSrc);
ShowMessage(sDest);
-- 메시지박스에 표시되는 문자:
헤헤헤\r\nTest임다
사용예제2:
String sSrc = "헤헤헤\\r\\nTest임다";
String sDest;
SrcToStr(sDest,sSrc);
ShowMessage(sDest);
-- 메시지박스에 표시되는 문자:
헤헤헤
Test임다
먼저 헤더 파일에는..
int __fastcall HexEncodeOpt(bool UseLowerCase);
int __fastcall HexEncode(String &Dest, const String& Src);
int __fastcall HexEncode(char *Dest, char *Src, int SrcSize);
int __fastcall HexDecode(String &Dest, const String& Src);
int __fastcall HexDecode(char *Dest, char *Src, int SrcSize);
int __fastcall SrcToStr(String &Dest, const String &Src);
int __fastcall StrToSrc(String &Dest, const String &Src);
요기까지..
cpp파일에는
static Byte HexEncodeTable[] = "0123456789ABCDEF";
static Byte HexEncodeTable2[] = "0123456789abcdef";
static Byte *HexTableSelected = HexEncodeTable;
static Byte HexDecodeTable[] = { 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
0, 0, 0, 0, 0,10,11,12,13,14,
15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,10,11,12,
13,14,15 };
static Byte ExtCodeKey[] = "ntvbra\\?\'\"";
static Byte ExtCodeValue[] = {10,9,11,8,13,7,92,63,39,34,0};
//---------------------------------------------------------------------------
int __fastcall HexEncodeOpt(bool UseLowerCase)
{
if (UseLowerCase)
{
HexTableSelected = HexEncodeTable2;
}
else
{
HexTableSelected = HexEncodeTable;
}
}
//---------------------------------------------------------------------------
int __fastcall HexEncode(String &Dest, const String& Src)
{
Dest.SetLength(Src.Length() * 2);
return HexEncode(Dest.c_str(), Src.c_str(), Src.Length());
}
//---------------------------------------------------------------------------
int __fastcall HexEncode(char *Dest, char *Src, int SrcSize)
{
Byte *pPtr = Src;
Byte *pEnd = pPtr+SrcSize;
while (pPtr < pEnd)
{
*Dest = HexTableSelected[(*pPtr) >> 4];
*(Dest+1) = HexTableSelected[(*pPtr) & 0x0F];
pPtr ++;
Dest += 2;
}
return SrcSize * 2;
}
//---------------------------------------------------------------------------
int __fastcall HexDecode(String &Dest, const String& Src)
{
Dest.SetLength(Src.Length() / 2);
return HexDecode(Dest.c_str(), Src.c_str(), Src.Length());
}
//---------------------------------------------------------------------------
int __fastcall HexDecode(char *Dest, char *Src, int SrcSize)
{
Byte *pPtr = Src;
Byte *pEnd = pPtr+SrcSize;
while (pPtr < pEnd)
{
*Dest = (HexTableSelected[(*pPtr)-48] << 4) | (HexTableSelected[(*(pPtr+1))-48]);
pPtr += 2;
Dest++;
}
return SrcSize / 2;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
Byte GetExtCodeValue(Byte *pPos)
{
Byte Value = *pPos;
Byte *pPtr = ExtCodeKey;
while (*pPtr)
{
if (*pPtr == Value)
return ExtCodeValue[pPtr - ExtCodeKey];
pPtr++;
}
return 0;
}
//---------------------------------------------------------------------------
Byte GetExtCodeKey(Byte Value)
{
Byte *pPtr = ExtCodeValue;
while (*pPtr)
{
if (*pPtr == Value)
return ExtCodeKey[pPtr - ExtCodeValue];
pPtr++;
}
return 0;
}
//---------------------------------------------------------------------------
bool IsHex(Byte* pPos)
{
if (*(pPos) == 'x')
{
for (int ii = 1; ii <= 2; ii++)
{
Byte Value = *(pPos+ii);
if ((Value < '0' || Value > '9') && (Value < 'a' || Value > 'f'))
return false;
}
return true;
}
else
return false;
}
//---------------------------------------------------------------------------
bool IsOct(Byte* pPos)
{
Byte Value = *(pPos);
if (Value >= '0' && Value < '4')
{
for (int ii = 1; ii <= 2; ii++)
{
Byte Value = *(pPos+ii);
if (Value < '0' || Value >= '8')
return false;
}
return true;
}
else
return false;
}
//---------------------------------------------------------------------------
int __fastcall SrcToStr(String &Dest, const String &Src)
{
Dest.SetLength(Src.Length());
Byte *pPtr = Src.c_str();
Byte *pDest = Dest.c_str();
Byte *pEnd = pPtr + Src.Length();
while (pPtr < pEnd)
{
if (*pPtr == '\\' && !(Src.IsTrailByte(pPtr - Src.c_str() + 1)))
{
if (IsHex(pPtr+1))
{
*pDest = (HexDecodeTable[(*(pPtr+2))-48] << 4) | (HexDecodeTable[(*(pPtr+3))-48]);
pPtr += 4;
}
else
{
if (IsOct(pPtr+1))
{
*pDest = ((*(pPtr+1)-48) << 6) | ((*(pPtr+2)-48) << 3) | (*(pPtr+3)-48);
pPtr += 4;
}
else
{
Byte Value = GetExtCodeValue(pPtr+1);
if (Value)
*pDest = Value;
else
return Src.c_str() - pPtr + 1;
pPtr += 2;
}
}
}
else
{
*pDest = *pPtr;
pPtr++;
}
pDest ++;
}
if (pDest - Dest.c_str() != Dest.Length())
Dest.SetLength(pDest - Dest.c_str());
return 0;
}
//---------------------------------------------------------------------------
int __fastcall StrToSrc(String &Dest, const String &Src)
{
Dest.SetLength(Src.Length()*4);
Byte *pDest = Dest.c_str();
Byte cValue;
int nSrcLen = Src.Length();
for (int ii = 1; ii <= nSrcLen; ii++)
{
cValue = Src[ii];
if (cValue < ' ' || cValue > '~' || cValue == '\\' || cValue == '\"')
// 제어코드가 있을 가능성이 있는 경우
{
if (Src.IsLeadByte(ii))
{
*pDest = cValue;
*(pDest+1) = Src[ii+1];
pDest += 2;
ii++;
continue;
}
else
{
*pDest = '\\';
pDest ++;
Byte cKey = GetExtCodeKey(cValue);
if (cKey) // 제어 코드가 있는 경우
{
*pDest = cKey;
pDest ++;
}
else
{
// 제어 코드가 없는 경우
*(pDest) = 'x';
*(pDest+1) = HexEncodeTable[cValue >> 4];
*(pDest+2) = HexEncodeTable[cValue & 0x0F];
pDest += 3;
}
}
}
else
{
*pDest = cValue;
pDest ++;
}
}
if (pDest - Dest.c_str() != Dest.Length())
Dest.SetLength(pDest - Dest.c_str());
return 0;
}
//---------------------------------------------------------------------------
|