C-Language/String Operations

C Language String Operations

Soul-Learner 2017. 1. 2. 17:26

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;
}