글쓴이 글 목록: opener

[C++ 정리] 2. 문자열 다루기 (C++스타일) 2015-06-17

기본 제공하는 자료형이 아닌 string 객체를 사용해 문자열을 다루는데 이는 간단하고 명료하다.

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	// 원본 문자열
	string orgString = "TEST STRING";
	
	cout << "STRING_LENGTH : " << orgString.size() << "\n";

	// 대상 문자열 & 복사
	string tarString = orgString;

	// 문자열 출력
	cout << "ORG_STRING : " << orgString << "\n";
	cout << "TAR_STRING : " << tarString << "\n";

	// 문자열 비교
	if (tarString == orgString){
		cout << "compare \"" << orgString << "\" :::: \"" << tarString << "\" indentical.\n";
	}
	else
	{
		cout << "compare \"" << orgString << "\" :::: \"" << tarString << "\" not indentical.\n";
	}

	// 문자열 결합
	string addString = "+ADD STRING";
	addString = orgString + addString;
	cout << "ADD_STRING : " << addString << "\n";

	// 문자열 찾기
	int stringIndex = addString.find("+ADD");
	// - 배열과 동일하게 생각하여 +ADD는 12번째 즉, 인덱스 11에 위치한다.
	cout << "FIND_STRING : " << stringIndex << "\n";

	// 문자열 자르기
	string cutString = addString.substr(12, 3);
	// - 대상 문자열의 12번째 부터 3자리의 글자를 읽어온다.
	cout << "CUT_STRING : " << cutString << "\n";

	return 0;
}
* <링크 : C스타일 문자열다루기>와 비교해 훨씬 간단명료해지고 많은 기능을 지원해줌을 알 수 있다.

[C++ 정리] 1. 문자열 다루기 (C스타일) 2015-06-16

C++로 개발하기 위해서는 C에서 사용하던 문자열 다루는 방식 또한 이해해야 한다. 이유는 많은 코드들이 C스타일로 개발되어 왔기 때문에 알고 있어야 하는 부분이다.

#include "stdafx.h"
#include <iostream>
#include <cstring>

using namespace std;

void TestChar(char* pointer);

int _tmain(int argc, _TCHAR* argv[])
{
	// 원본 문자열
	char orgString[] = "TEST STRING";
	// ※ 배열이름은 첫번째 문자열의 주소와 같다.	
	int orgStringLen = strlen(orgString);
	int orgStringAddrLen = strlen(&orgString[0]);
	cout << "STRING_LENGTH : " << orgStringLen << " :: " << orgStringAddrLen << "\n";

	// 대상 문자열
	// - strlen함수는 마지막 NULL문자는 포함하지 않고 카운트하므로 +1을 해준다.
	char* tarString = new char[orgStringLen+1];

	// 문자열 복사
	// Error - This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
	// strcpy(tarString, orgString);
	// ※ 안전한 스트링 함수의 사용 : tarString크기를 넘어서는 메모리 복사를 방지
	strcpy_s(tarString, orgStringLen + 1, orgString);

	// 문자열 출력
	// ※ 배열이름은 첫번째 문자열의 주소라고 하였으나 문자열이 출력되는 이유는?
	//    ==> 문자열의 첫번째 바이트의 주소가 전체 문자열을 지칭하기 때문
	cout << "ORG_STRING : " << orgString << "\n";
	cout << "TAR_STRING : " << tarString << "\n";

	// 배열이름이 첫번째 문자열의 주소가 되어 문제가 되는점
	// - TestChar함수에서는 문자열인지 문자인지 알수가 없다.
	char singleChar = 'A';
	char multiChar[] = "ABCDEFG";
	TestChar(&singleChar);
	TestChar(multiChar);

	// 문자열 비교
	if (strcmp(orgString, tarString) == 0){
		cout << "compare \"" << orgString << "\" :::: \"" << tarString << "\" indentical.\n";
	}
	else
	{
		cout << "compare \"" << orgString << "\" :::: \"" << tarString << "\" not indentical.\n";
	}

	// 문자열 결합
	char addString[] = "+ADD STRING";
	int addStringLen = strlen(addString);
	// int addStringLen = strlen(&addString[0]);
	char* tempString = new char[orgStringLen + addStringLen + 1];

	// 문자열 복사 + 결합
	// Error - This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
	// strcat(orgString, addString);
	// ※ 안전한 스트링 함수의 사용 : tarString크기를 넘어서는 메모리 복사를 방지
	strcpy_s(tempString, orgStringLen + addStringLen + 1, orgString);
	strcat_s(tempString, orgStringLen + addStringLen + 1, addString);
	
	if (strcmp(tarString, tempString) == 0){
		cout << "compare \"" << tarString << "\" :::: \"" << tempString <<"\" indentical.\n";
	}
	else
	{
		cout << "compare \"" << tarString << "\" :::: \"" << tempString << "\" not indentical.\n";
	}

	// 문자열 선언
	char declareArray[] = "ABCD";
	char* declarePointer = "ABCD";
	// 시작주소
	void* startAddrArray = declareArray;
	void* startAddrPointer = declarePointer;
	// 끝주소
	void* endAddrArray = declareArray+3;
	void* endAddrPointer = declarePointer+3;
	
	// 값의 출력 결과는 동일하지만 메모리에 저장되는 구조는 다르다.
	// ==> pointer로 선언할 경우 문자열 덩어리를 가리키는 값이 메모리에 따로 저장됨
	// ==> (&declarePointer != startAddrPointer)
	cout << "Value : " << declareArray << "\n";
	cout << "Array Addr : " << &declareArray << "(" << startAddrArray << "~" << endAddrArray << ")" << "\n";
	cout << "Value : " << declarePointer << "\n";
	cout << "Pointer Addr : " << &declarePointer << "(" << startAddrPointer << "~" << endAddrPointer << ")" << "\n";

	return 0;
}

void TestChar(char* pointer)
{
	// pointer는 단일문자인지 문자열인지 알수가 없다.
	// ==> 해결책은 주석을 통해서 약속한다.
	// pointer가 가리키는 변수가 단일문자라면 
	// NULL문자를 포함하고 있지 않기 때문에 쓰레기값이 뒤에 붙어서 출력된다.	
	cout << "TestChar : " << pointer << "\n";
	// 단일 문자를 출력하기 위해서는 앞에 *를 붙인다.
	cout << "TestChar : " << *pointer << "\n";
}
* 배열의 이름은 첫번째 문자열의 주소와 같다. ( orgString == &orgString[0] )
* strlen 함수는 마지막 NULL문자를 제외하고 카운트 하는 점을 유념한다.
* 문자열 함수를 다루는 법을 익힌다. (strlen, strcpy, strcmp, strcat)
* char declareArray[] = "ABCD";	char* declarePointer = "ABCD"; 두가지 선언법의 메모리 구조는 다르다. (아래그림 참조)
* 배열이름이 첫번째 문자열의 주소가 되기 때문에 유의해야 한다. (TestChar 함수의 주석 참조)

character

[문제해결] UTF-8 + BOM 인코딩 2015-06-16

1. 문제상황

> json파일을 업로드하여 데이터를 저장하는 툴을 개발중에 전달받은 샘플 파일이 파싱이 되지 않는 문제가 발생하였다.

$readJson = file_get_contents($_FILES['json_file']['tmp_name']);

2. 문제확인

> 파일은 정상적으로 업로드 되는것으로 보여 읽어드린 스트링을 로그로 남겨 보았다.

DEBUG - 2015-06-16 06:38:33 --> 癤?
	{
		"index":1,
		"name":"a",
		"gold":0,
		"resource":"A"
	},
	{
		"index":2,
		"name":"b",
		"gold":1000,
		"resource":"B"
	},
	{
		"index":3,
		"name":"c",
		"gold":2000,
		"resource":"C"
	}
]

* 앞의 첫글짜가 깨져있다.

> 샘플파일의 인코딩을 확인해 보니 UTF-8 + BOM 형태였다. 윈도우즈의 특정 에디터(메모장 등)로 저장시 자동으로 BOM(Byte Of Mark)을 집어 넣는다고 한다. BOM은 유니코드 인코딩 방식인 UTF-8, UTF-16, UTF-32의 문서 맨앞에 특정 바이트를 넣어 인코딩 방식을 명시 하는 것으로 {UTF-8 : “EF BB BF”}, {UTF-16(Big Endian) : “FE FF”}, {UTF-16(Little Endian) : “FF FE”}, {UTF-32(Big Endian) : “00 00 FE FF”}, {UTF-32(Little Endian) : “FF FE 00 00”} 이 문서 맨에 붙게 된다.

UTF-8 BOM


3. 문제해결

> 파일을 읽어들여 다음 코드로 BOM을 제거 한 후 파싱을 하도록 한다.

$readJson = file_get_contents($_FILES['json_file']['tmp_name']);
				
// BOM 필터링
if (substr($readJson, 0, 3) == "\xEF\xBB\xBF") {
	$readJson = substr($readJson, 3);
}