[C] C언어 공부하기 12. 문자열 함수

2020. 1. 6. 16:50C,C++

출처 : http://www.soen.kr/

 

SoEn:소프트웨어 공학 연구소

 

www.soen.kr

12-1. 문자열 함수

12-1-가. 문자열 복사

str1, str2, str3가 문자형 배열일 때 다음 코드는 모두 동작하지 않는다.

 

str1="Korea";                    // 문자열 상수를 대입할 수 없음

str1=str2;                         // 배열끼리도 대입할 수 없음

str3=str1+str2;                        // +연산자로 문자열을 연결할 수 없음

if (str1 == str2)                       // ==연산자로 문자열끼리 비교할 수 없음

 

문자열 함수들은 대부분 string.h에 선언되어 있으므로 이 헤더 파일을 인클루드해야 한다.

다음 함수는 문자 배열에 문자열을 복사하는데 문자열 함수 중에 가장 기본이 되는 함수이다.

 

char *strcpy(char *dest, const char *src);

dest는 복사될 목적지이고 src는 복사될 원본이므로 이 함수가 하는 동작을 간단하게 표현하면 dest=src라고 할 수 있다.

 

strcpy 함수는 src 문자열 전체를 널 종료 문자를 만날 때까지 dest로 복사한다. 이에 비해 다음 함수는 지정한 길이만큼만 복사한다.

 

char *strncpy(char *dest, const char *src, size_t count);

 

strcpy 함수의 원형과 유사하되 이름 중간에 개수를 지정할 수 있다는 의미의 알파벳 n이 삽입되었으며 세 번째 인수 count가 더 추가되어 있다. count는 복사할 문자 개수를 지정하는데 정확하게 이 개수만큼만 dest로 복사되며 널 종료 문자는 따로 덧붙이지 않는다. 

 

다음은 문자열의 길이를 조사하는 strlen 함수를 알아보자. 문자열의 길이란 문자열 시작 번지에서부터 시작해서 널 종료 문자까지 들어 있는 문자의 개수를 의미한다.

 

size_t strlen(const char *string);

 

 

 함수가 조사하는 문자열의 길이는 배열에 실제 저장된 문자의 개수이지 배열의 크기가 아니다. 배열 자체의 크기를 구할 때는 sizeof 연산자를 사용해야 한다. 

 

12-1-나. 문자열 연결

문자열 연결이란 한 문자열 끝에 다른 문자열을 덧붙이는 것이다. 

 

char *strcat(char *dest, const char *src);

char *strncat(char *dest, const char *src,size_t count);

 

strcat 함수는 dest 문자열 뒤에 src문자열을 덧붙인다. 

strncat 함수는 문자열을 합치되 합칠 문자열의 개수를 지정할 수 있다. 

 

12-1-다. 문자열 비교

문자열 비교 함수는 두 문자열이 같은지 다른지, 다르다면 어떤 문자열이 더 큰지를 비교한다.

 

int strcmp(const char *s1, const char *s2);

int strncmp(const char *s1, const char *s2, size_t count);

 

이 함수들은 인수로 주어진 두 문자열을 비교한 후 그 결과를 다음과 같이 정수값 하나로 리턴한다.

 

s1과 s2가 같으면 0

s1>s2 이면 양수

s1<s2 이면 음수

 

strncmp 함수는 가운데 n이 끼어 있는데 strncpy, strncat 함수들과 마찬가지로 지정한 개수만큼만 문자열을 비교한다. 

영문에는 대소문자라는 것이 있어서 똑같은 문자열이라도 대소문자 구성에 따라 다른 문자열로 인식될 수도 있다. 

다음 두 함수는 대소문자 구분없이 문자열을 비교한다.

 

int stricmp(const char *string1, const char *string2);

int strnicmp(const char *string1, const char *string2, size_t count);

함수명 중간에 i가 삽입되어 있는데 여기서 i는 Ignore, 즉 대소문자 구성을 무시하고 비교한다는 뜻이다. 

 

12-1-라. 문자열 검색

문자열 검색 함수는 문자열 중 특정 문자나 부분 문자열의 위치를 찾아 주는데 다음과 같은 것들이 있다. 첫 번째 인수로 검색 대상 문자열을 주고 두 번째 인수로 검색할 문자(열)을 주며 리턴값은 모두 문자형 포인터이다.

 

char *strchr(const char *string, int c);

char *strrchr(const char *string, int c);

char *strstr(const char *string, const char *strSearch);

char *strpbrk(const char *string, const char *strCharSet );

char *strtok(char *strToken, const char *strDelimit);

 

str이 "notebook"일 때 strchr(str,'b') 함수는 다음과 같이 동작한다.

문자형 포인터를 리턴하므로 이 번지를 대입한 후 검색된 위치에 대해 어떤 작업을 할 수 있다. 다음 코드는 "notebook" 문자열에서 b 문자를 찾아 c로 바꾼다. 결국 str은 "notecook"이 될 것이다.

 

char str[]="notebook";

char *ptr=strchr(str,'b');

*ptr='c';

 

 문자열중 특정 문자의 위치는 관심없고 단지 문자의 포함 여부만 알고 싶을 때는 리턴값이 NULL인지 아닌지만 비교하면 된다.

 

if (strchr(Name,'a')!=NULL) {

     puts("이름에 a문자가 포함되어 있군요.");

}

 

 

strchr 함수는 항상 주어진 문자열의 선두에서부터 문자를 찾는다. 이에 비해 strrchr 함수는 문자열의 뒤에서부터 문자를 검색하는데 함수명에 포함된 r은 rear, 즉 뒤쪽이라는 뜻이다.

 두 문자열이 정확하게 같은지 비교해서는 안되며 부분 문자열이 포함되어 있는지를 조사해야 하는데 이럴 때 바로 strstr 함수가 사용된다.

 

if (strstr(language,"영어") != NULL) { 영어 가능함 }

strstr 함수는 부분 문자열을 검색하므로 "영어, 일본어"는 물론이고 "일본어, 영어"와 같이 순서를 바꿔 써도 이 문자열이 포함되어 있는지 검색할 수 있다.

 

strpbrk 함수는 첫 번째 인수로 주어진 문자열에서 두 번째 인수로 주어진 문자열에 속해 있는 문자 중 가장 먼저 발견된 문자를 찾아 그 번지를 리턴한다.

char str[]="Four score and seven years ago";

char *ptr=strpbrk(str,"def");

 

str에 긴 문자열이 들어 있는데 이 문자열 중에서 d나 e나 f중 가장 먼저 발견되는 문자를 찾아 ptr에 대입한다. 이 경우 str[9]에 있는 'e'가 검색될 것이다.

 

strtok 함수는 문자열을 토큰으로 잘라낸다. 예를 들어 "서울/대전/대구/부산" 문자열을 "/" 구분자로 자르면 서울, 대전, 대구, 부산 4개의 문자열로 분할할 수 있다. 

실행 결과는 다음과 같다.

 

strtok 함수는 최초 호출될 때 문자열의 첫 번째 토큰을 찾고 두 번째 토큰 위치를 NULL문자로 만든 후 토큰의 포인터를 리턴한다.

(** 다음과같이 좀더 가독성 좋게 만들 수도 있는 것 같다. **)

 

12-1-마. 문자열 변환

문자열 변환이란 문자열의 내용을 일정한 규칙에 따라 바꾸는 것이다. 다음 다섯 가지의 함수가 있는데 아주 쉬우므로 간략하게 설명하도록 한다.

 

char *strset(char *string, int c);

char *strnset(char *string, int c, size_t count);

char *strlwr(char *string);

char *strupr(char *string);

char *strrev(char *string);

 

strset 함수는 문자열을 c문자로 가득 채운다. 

str이 "password" 문자열을 가질 때 strset(str,'*')는 이 문자열을 "********"로 바꿀 것이다.

strnset 함수는 개수를 지정할 수 있다는 것만 다르다.

strlwr 함수는 모든 문자를 소문자로 바꾸며 strupr 함수는 모든 문자를 대문자로 바꾼다.

strrev 함수는 문자열을 거꾸로 뒤집는다. 즉, 제일 처음에 있는 문자를 마지막 문자와 교환하고 두 번째 문자는 마지막 두 번째 문자와 교환하는 식이다. 

 

12-1-바. 문자 관리 함수

다음은 문자열을 구성하는 개별 문자들을 관리하는 함수에 대해 알아보자. 

이 함수들은 인수로 전달된 문자가 특정 그룹에 속하는지 조사하는데 그룹에 속하면 0이 아닌 값을 리턴하고 그렇지 않다면 0을 리턴한다.

특정 문자가 알파벳인지, 숫자인지 등을 판단하고 싶을 때 이 함수들을 호출한다. 

다음 두 함수는 영문 대소문자를 변환한다.

 

int tolower(int c);

int toupper(int c);

 

만약 c가 영문자가 아니라면 아무 동작도 하지 않으며 c를 그대로 리턴한다. 

 

12-1-사. 메모리 관리 함수

다음 함수들은 메모리 관리 함수들이다

 

void *memcpy(void *dest, const void *src, size_t count);

int memcmp(const void *buf1,const void *buf2,size_t count);

void *memchr(const void *buf,int c,size_t count);

void *memset(void *dest,int c,size_t count);

void *memmove(void *dest,const void *src,size_t count);

 

strcpy에 대응되는 memcpy, strcmp에 대응되는 memcmp 등의 함수들이 있는데 기본적인 동작 방식은 대응되는 문자열 함수와 같다고 생각하면 된다. 다만 몇 가지 점만 다른데 메모리 관리 함수들과 문자열 관리 함수들의 차이점은 다음과 같다.

1) 인수와 리턴값의 타입이 다르다. 문자열 관리 함수들은 항상 문자열을 대상으로 하므로 취하는 인수나 리턴값이 대부분 char *형이지만 메모리 조작 함수들은 임의의 값을 대상으로 하기 때문에 인수와 리턴값이 모두 void *형이다.

2) 문자열은 시작 번지만 알려 주면 널 종료 문자를 끝으로 인식하기 때문에 길이를 별도로 알려줄 필요가 없다. 하지만 메모리 관리 함수는 길이를 알려 주지 않으면 어디까지가 작업 대상인지를 알지 못한다. 

 

int ar[10];

memset(ar,0,sizeof(ar));

 

배열을 선언하면서 초기화하지 않았거나 또는 실행중에 이미 값이 저장된 배열을 모두 특정 값으로 채우고자 할 때 memset 함수를 사용한다. 

메모리끼리 복사할 때는 memcpy 함수를 사용한다. 복사할 원본과 대상, 그리고 복사할 길이를 인수로 전달하면 두 메모리의 내용을 완전히 똑같이 만들어 준다.

배열은 서로 대입할 수 없으며 배열의 사본을 만들려면 배열 요소끼리 개별적으로 대입해야 하는데 memcpy 함수를 사용하면 배열끼리도 한 번에 복사할 수 있다. 

(** 여기서 memcpy가 값을 복사하는건지 주소를 복사하는건지 궁금했다. **)

실험을 위한 코드

실험결과 주소를 복사하는게 아니라 값을 복사한다는 걸 알게 됨.

 

메모리 관리 함수중에 가장 재미있고도 실용적인 함수는 memmove 함수이다. 이 함수는 메모리의 내용을 지정한 길이만큼 다른 곳으로 옮긴다.

12-2-가. 정수와 문자열

수치와 문자열은 데이터 타입이 다르며 따라서 메모리에 기억되는 모양도 상당히 다르다. 다음 두 변수를 보자.

 

int num=123;

char str[]="123";

 

두 변수 모두 123으로 초기화되었는데 정수형 변수 num은 수치값 123을 가지고 있고 문자 배열 str은 문자열 "123"을 가지고 있다. 이 두 값은 외형적인 모양이 비슷해 보이지만 내부적으로는 완전히 다른 값이다. 

문자열과 수치는 내부적인 모양이 완전히 다르기 때문에 상호 대입하거나 연산할 수 없다. 

콘솔 환경에서는 이런 두 형식의 차이점을 어느 정도 완충해주는 장치가 있다. 화면으로 직접 출력 가능한 형식은 문자열뿐인데 printf 함수는 %d, %f 서식에 의해 정수나 실수를 문자열로 바꾸어 출력한다. 

다음 함수들은 정수를 문자열로 변환한다. 

char *itoa(int value, char *string, int radix);

char *ltoa(long value, char *string, int radix);

char *ultoa(unsigned long value, char *string, int radix);

(** 그 외에도 sprintf 라는 방법도 있는 듯 하다. **)

https://dojang.io/mod/page/view.php?id=388

 

C 언어 코딩 도장: 46.4 정수를 문자열로 변환하기

이번에는 정수를 문자열 형태로 변환하는 방법입니다. sprintf 함수를 사용하면 정수를 문자열로 변환할 수 있습니다(stdio.h 헤더 파일에 선언되어 있습니다). sprintf(문자열, "%d", 정수); sprintf(문자열, "%x", 정수); sprintf(문자열, "%X", 정수); 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요. integer_to_string.c #define _CRT_SECURE_NO_WARNINGS // spr

dojang.io

itoa는 c언어 표준 함수는 아니다. 표준 함수는 sprintf라 한다.

 

다음은 문자열을 정수로 바꾸는 함수에 대해 알아보자.

int atoi(const char *string);

long atol(const char *string);

long strtol(const char *nptr, char **endptr, int base);

unsigned long strtoul(const char *nptr, char **endptr, int base);

 

문자열 str에 "123"이 저장되어 있을 때 atoi(str)은 수치값 123을 리턴한다. 

atoi("123"), atoi("-78") 이런 식의 문자열을 전달해야 제대로 변환된다. atoi("aaa")처럼 수치가 아닌 문자열을 전달할 경우 atoi는 변환할 수 없다는 의미로 0을 리턴한다.

만약 수치와 문자열이 섞여 있다면 atoi는 에러를 리턴하는 대신 변환 가능한 부분까지만 변환한다. 예를 들어 atoi("12month")를 호출하면 12가 리턴된다. 

 

12-2-나. 실수와 문자열

실수는 정수에 비해 소수점 이하를 가질 뿐만 아니라 부동 소수점 표기법이 다소 복잡해서 정수보다는 변환하기가 훨씬 더 까다롭다. 다음 함수들은 실수를 문자열로 변환한다.

char *gcvt(double value, int digits, char *buffer);

char *ecvt(double value, int count, int *dec, int *sign);

char *fcvt(double value, int count, int *dec, int *sign);

 

다음 함수는 문자열을 실수로 변환한다. 두 함수는 기능이 거의 동일하되 strtod 함수는 변환 불가 문자를 만날 때 그 위치를 endptr로 리턴한다는 정도만 다르다. 편의성면에서는 atof가 좀 더 사용하기 쉽다.

 

double atof(const char *string);

double strtod(const char *nptr, char **endptr);