C 언어 문자열 다루기 예제
https://en.wikipedia.org/wiki/C_string_handling#wchar_t
문자열 선언
문자배열을 사용하면 배열이름은 포인터 상수로 취급되어 재할당할 수 없으나 문자열 자체는 일반 배열의 원소처럼 자유롭게 변경할 수 있다. 문자배열을 사용하여 문자열을 선언하면 해당 문자열은 일반 변수가 저장되는 스택이나 데이터 세그먼트에 저장된다.
반면에 문자 포인터를 사용하면 포인터 변수는 상수로 취급되지 않으므로 해당 포인터 변수에는 다른 주소로 재할당될 수 있으나 문자열 자체는 상수가 되어 문자열을 구성하는 문자는 변경할 수 없다. 포인터를 이용하여 문자열을 선언하면 해당 문자열은 Literal Pool에 저장되므로 동일한 문자열이 중복해서 생성될 수 없다
byte string(char), wide string(wchar_t) 형으로 구분된다. wchar_t는 최소한 16비트를 차지한다
- byte string은 string.h(C언어), cstring(C++) 헤더에 선언되어 있다
- wide string은 wchar.h(C언어), cwchar(C++) 헤더에 선언되어 있다
- 문자열이 저장된 메모리와 관련한 작업을 수행하는 함수의 이름은 mem으로 시작된다
예) memset(), memcpy()
- 문자열에 대한 작업을 수행하는 함수의 이름은 str 으로 시작된다
예) strlen(), strcat()
- 와이드 문자열이 저장된 메모리와 관련한 작업을 수행하는 함수의 이름은 wmem 으로 시작된다
예) wmemset(), wmemcpy()
- 와이드 문자열에 대한 작업을 수행하는 함수의 이름은 wcs로 시작된다
예) wcslen(), wcscat()
#include <stdio.h> int main() { // 배열변수는 포인터 상수로 취급 char chArr1[] = {'H','e','l','l','o','\0'}; char chArr2[] = "Hello"; // 컴파일러에 의해 배열로 변환되며 자동으로 뒤에 '\0' 가 추가된다 //배열변수에는 첫번째 원소의 주소가 저장된다 chArr1[0] = 'h'; printf("%s ", chArr1); // hello chArr2[0] = 'h'; printf("%s ", chArr2); // hello return 0; }
포인터를 이용한 문자열의 선언
#include <stdio.h> #include <string.h> // strcmp() #pragma warning(disable:4996) char* getStr(); int main() { // 문자열 포인터를 이용하여 선언된 문자열은 Literal Pool에 저장됨 char* str1 = "Hello"; char* str2 = "Hello"; printf("str1의 주소=%p \n", str1); // 문자열 2개의 주소가 완전히 동일함 printf("str2의 주소=%p \n", str2); printf("str1==str2:%d \n", str1 == str2); // 1(true), 주소가 일치함 printf("strcmp(str1,str2):%d \n", strcmp(str1,str2)); // 0(내용이 동일함) // 문자열 배열을 이용하여 선언된 문자열은 Stack 이나 Data Segment에 저장됨 char arr1[] = "Hello"; char arr2[] = "Hello"; printf("arr1의 주소=%p \n", arr1); // 2개의 배열의 주소는 서로 다름 printf("arr2의 주소=%p \n", arr2); printf("arr1==arr2:%d \n", arr1 == arr2); // 0(false), 주소가 다름 printf("strcmp(arr1,arr2):%d \n", strcmp(arr1, arr2)); // 0(내용이 동일함) char* s = getStr(); printf("s의 주소=%p \n", s); // 문자열 2개의 주소가 완전히 동일함 printf("str1의 주소=%p \n", str1); return 0; } char* getStr() { return "Hello"; // 함수 내에서 선언한 문자열 포인터이므로 Literal Pool에 저장됨 }
문자열의 키보드 입력 및 화면 출력(표준 입출력)
#include <stdio.h> int main() { char * userId[32]; char * userPwd[32]; char c; printf("아이디:"); scanf("%s", userId); // 입력버퍼를 비움 while ((c = getchar()) != '\n' && c != EOF) {} printf("입력된 ID:"); puts(userId); //개행됨 puts("암호입력:"); //개행됨 gets(userPwd); puts("입력된 암호:"); // 개행됨 puts(userPwd); //개행됨
return 0; }
stdio.h 의 문자열 입출력 함수(아래의 함수 외에 파일 입출력 함수도 포함)
- int printf(const char *format, ...)
- int sprintf ( char *buff, const char *format, ... ) : buff에 형식지정한 데이터를 저장하고 마지막에 \0를 붙인다
- int snprintf ( char *buff, size_t n, const char *format, ... ) : 최대 n 바이트를 저장. 마지막에 \0를 붙인다
- int vprintf(const char *format, va_list arg)
- int vsprintf(char *str, const char *format, va_list arg)
- int scanf(const char *format, ...)
- int sscanf(const char *str, const char *format, ...)
- int getchar(void)
- char *gets(char *str)
- int putc(int char, FILE *stream)
- int putchar(int char)
- int puts(const char *str)
wchar.h :와이드 문자를 다루는 모든 함수들 (표준 입출력, 파일 입출력, 문자열 입출력, 메모리 관련 함수들)
- int fwprintf(FILE *, const wchar_t *, ...)
- int fwscanf(FILE *, const wchar_t *, ...)
- wint_t getwchar(void);
- wint_t putwchar(wchar_t);
- int swprintf(wchar_t *, size_t, const wchar_t *, ...);
- int swscanf(const wchar_t *, const wchar_t *, ...);
- size_t wcslen(const wchar_t *);
- wchar_t* wmemset(wchar_t *, wchar_t, size_t);
- int wprintf(const wchar_t *, ...);
- int wscanf(const wchar_t *, ...);
string.h 의 문자열을 다루는 주요 함수
- size_t strlen ( const char *str ) : length
- char * strlwr ( char *str ) : lower
- char * strupr ( char *str ) : upper
- char * strcat ( char *destination, const char *source ) : catenate
- char * strncat ( char *destination, const char *source, size_t count ) : first n characters
- char * strcpy ( char *destination, const char *source )
- char * strncpy ( char *destination, const char *source, size_t count) : first n characters
- int strcmp ( const char *str1, const char *str2 ) : compare
- int strncmp ( const char *str1, const char *str2, size_t maxcount ) : first n characters
- int strcmpi ( const char *str1, const char *str2 ) : ignores case
- int stricmp ( const char *str1, const char *str2 ) : ignores case
- int strnicmp ( const char *str1, const char *str2, size_t maxcount ) : first n characters, ignores case
- char * strdup ( const char *source ) : Duplicating. 리턴된 복사본은 문자편집이 가능함
- char * strchr ( const char *str, int ch ) : first occurrence
- char * strrchr ( const char *str, int ch ) : last occurrence(reverse)
- char * strstr ( const char *str, const char *substr )
- char * strrstr ( const char *str, const char *substr ) : last occurrence(reverse)
- char * strset ( char *str, int ch ) : 주어진 문자로 문자열의 모든 문자를 변경한다
- char * strnset ( char *str, int ch, int maxcount ) : first n characters
- char * strrev ( char *str ) : reverses a string
- char * strtok(char *str, const char *delim) : 처음 호출시 str에 문자열을 전달하고 이후에는 NULL 전달
- void * memchr(const void *str, int c, size_t n)
- int memcmp(const void *str1, const void *str2, size_t n)
- void * memcpy(void *dest, const void *src, size_t n)
- void * memmove(void *dest, const void *src, size_t n)
- void * memset ( void * ptr, int ch, size_t num ) : ptr 이 가리키는 곳에 저장된 문자 num개를 ch로 변경
stdlib.h
- double atof(const char *str)
- int atoi(const char *str)
- long int atol(const char *str)
- int mblen(const char *str, size_t n)
- size_t mbstowcs(schar_t *pwcs, const char *str, size_t n)
- int mbtowc(whcar_t *pwc, const char *str, size_t n)
- size_t wcstombs(char *str, const wchar_t *pwcs, size_t n)
- int wctomb(char *str, wchar_t wchar)
위의 문자열 함수 테스트
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <wchar.h> #pragma warning(disable: 4996) int main() { char str[] = "Hello"; size_t size = strlen(str); printf("strlen(%s)=%d \n", str, (int)size); // 5 char* str2 = strlwr(str); printf("strlwr(%s)=%s \n", str, str2); // str, str2 둘다 소문자, 동일함 printf("str의 주소:%p, str2의 주소:%p \n", str, str2); // 주소까지 동일함 str2 = strupr(str); printf("strupr(%s)=%s \n", str, str2); printf("str의 주소:%p, str2의 주소:%p \n", str, str2);// 주소까지 동일함 char buf[32] = "Hello"; // 두 문자열을 합치기에 충분한 공간이 필요함 str2 = strcat(buf, "World"); printf("%s, %s \n", str2, buf); // 동일 printf("buf의 주소:%p, str2의 주소:%p \n", buf, str2);// 주소까지 동일함 memset(buf, 0, 32); // buf의 공간 32개를 0으로 설정함 str2 = strncat(buf, "World", 3); printf("%s, %s \n", buf, str2); // Wor, Wor printf("buf의 주소:%p, str2의 주소:%p \n", buf, str2);// 주소까지 동일함 str2 = strcpy(buf, "ABCDEF"); // 버퍼의 첫공간부터 채운다 printf("%s, %s \n", buf, str2); printf("buf의 주소:%p, str2의 주소:%p \n", buf, str2);// 주소까지 동일함 memset(buf, 0, 32); str2 = strncpy(buf, "ABCDEF", 3); printf("%s, %s \n", buf, str2); // ABC, ABC printf("buf의 주소:%p, str2의 주소:%p \n", buf, str2);// 주소까지 동일함 int res = strcmp("Hello", "Hello"); printf("문자열 비교결과=%d \n", res); // 0(동일) res = strcmp("Hello", "World"); printf("문자열 비교결과=%d \n", res); // -1(왼쪽 문자열이 작음, 사전의 순서상 앞선다) res = strcmp("World", "Hello"); printf("문자열 비교결과=%d \n", res); // 1(왼쪽 문자열이 큼, 사전의 순서상 뒤에 옴) res = strncmp("Hel", "Hello", 3); printf("문자열 비교결과=%d \n", res); // 0 res = strcmpi("hello", "Hello"); printf("문자열 비교결과=%d \n", res); // 0 res = stricmp("hello", "Hello"); printf("문자열 비교결과=%d \n", res); // 0 res = strnicmp("he", "Hello", 2); printf("문자열 비교결과=%d \n", res); // 0 char* strSrc = "C Language Programming"; char* pStr = strdup(strSrc); pStr[0] = 'c'; // 복사된 문자열은 편집 가능함 // 원본 문자열과 사본 문자열은 주소가 서로 다름 printf("원래 문자열 주소:%p, 복사된 문자열주소:%p \n", strSrc, pStr); str2 = strchr("Hello World", 'o'); //발견된 첫 위치를 char* 로 리턴한다 printf("리턴된 포인터=%s \n", str2); // o World str2 = strrchr("Hello World", 'o'); // 뒤에서 부터 검색. 없으면 NULL printf("리턴된 포인터=%s \n", str2); // orld str2 = strstr("Hello World", "World"); // 비표준 함수 printf("리턴된 포인터=%s \n", str2); // World printf("buf=%s \n", buf); // ABC str2 = strset(buf, 'A'); // buf 의 모든 문자를 'A'로 설정한다(문자가 아닌곳은 제외) printf("buf=%s \n", buf); // AAA memset(buf, 'A', 31); // buf의 전체공간을 A로 채운다 printf("buf=%s \n", buf); // A....... str2 = strnset(buf, 'B', 3); // buf의 문자 3개를 B로 설정하고 나머지는 그대로 둔다 printf("buf=%s \n", buf); // BBBAAAA...... char str3[] = "World"; str2 = strrev(str3); // 문자열을 뒤집는다. 비표준 함수 printf("뒤집혀진 문자열=%s, %s \n", str2, str3); // dlroW, dlroW printf("str3의 주소:%p, str2의 주소:%p \n", str3, str2);// 주소까지 동일함 // 문자열을 생성할 때 char* 형을 사용하면 문자열 상수가 되어 해당 문자열의 // 문자는 변경할 수 없다. // 문자열을 생성할 때 char 배열을 사용하면 배열변수는 포인터 상수이지만 // 배열 안에 있는 문자는 변경이 가능하다 char* str4 = "Thank you"; // 문자열 상수이기 때문에 문자열을 구성하는 문자는 변경할 수 없다 printf("str4[0]=%c \n", str4[0]); // T //str4[0] = 't'; // 오류. 문자열 상수는 변경할 수 없다 //printf("str4=%s \n", str4); // str4 = "문자 포인터 변수는 상수가 아니므로 다른 포인터로 재할당될 수 있다"; printf("str4=%s \n", str4); // 정상 출력됨 char info[] = "No|Name|Email|Phone"; char* token = strtok(info, "|"); while (token != NULL) { printf("token=%s \t", token); token = strtok(NULL, "|"); } printf("\n"); int num = atoi("100"); printf("atoi()결과=%d \n", num); // 100 int aInt = 368; char str5[15]; sprintf(str5, "%d", aInt); printf("sprintf(), 숫자->문자열 변환결과=%s \n", str5); // 368 aInt = 123456; snprintf(str5, 4, "%d", aInt); // 맨뒤에 '\0'가 포함되므로 123만 쓰여짐 printf("snprintf(), 숫자->문자열 변환결과=%s \n", str5); wchar_t wbuff[20]; swprintf(wbuff, 5, L"%d", 100000); wprintf(L"%s", wbuff); // 1000 return 0; }