C 언어 | 고급 기능 | 문자열 편집 - strcat(), strlen(), strcpy(), strcmp() 등
여러 문자열을 결합하거나 문자열의 일부를 다른 문자열을 삽입하는 것과 같은 작업은 문자열 작업을 수행 표준 함수를 사용한다.
문자열을 추가 및 변환 처리
일반적으로 많은 고급 언어는 직관적인 문자열 조작 기능을 제공한다. 인간의 감성을 생각하면 다음과 같은 문장은 문자열의 추가 처리인 것을 기대할 것이다.
"오늘은 " + 2017 + "년 "+ 11 + "월 " + 26 + "일 입니다"
이 수식은 여러 문자열과 숫자로 구성되어 있다. 고급 언어의 대부분은 식을 최종적으로 문자열로 변환한다.
"오늘은 2017년 11월 26일 입니다"
많은 프로그래머는 문자열의 덧셈 연산이 가능한 결과를 원할 것이다. 그러나, C 언어의 경우는 문자열 리터럴의 실체는 문자 배열이므로 배열의 앞에 포인터로 처리된다. 따라서 문자열 편집하려면 직접 배열을 조작할 수 밖에 없다. 문자열을 추가하려면 충분한 메모리 공간을 할당하고, 문자열의 맨 끝에서 추가할 문자열의 각 문자를 순서대로 대입해 나갈 필요가 있다. 문자열의 일부를 잘라내거나 숫자와 문자열의 상호 변환을 할 때도 마찬가지이다.
그러나, 이를 모두 자기 부담으로 준비하는 것은 효율적이지 않다. 그래서 표준 라이브러리는 문자열을 조작하기 위한 기본적인 함수들을 제공하고 있다. 문자열 관련 함수는 string.h 헤더 파일에 선언되어 있다. 기본적인 문자열의 추가는 strcat() 함수를 사용한다.
strcat() 함수
char *strcat( char *string1, const char *string2 );
strcat() 함수는 string1에 string2를 추가한다. 이 함수의 반환 값은 string1과 동일한 것이고, 에러 값을 반환하는 것은 아니다. string1와 string2은 NULL로 끝나는 문자 배열이어야 한다.
함수는 string1의 NULL 문자를 string2의 앞에 문자열로 덮어쓰고, 그 후의 문자열을 추가하는 것이다. string1에 할당된 메모리 공간이 부족하기도 하고, string1과 string2가 겹쳐져 있는 경우의 동작은 정의되지 않는다. 즉, string1과 string2는 원칙적으로 다른 문자 배열에 대한 포인터이어야 한다.
코드1
#include <stdio.h>
#include <string.h>
int main() {
char str1[256] = "Kitty on your lap ";
strcat(str1 , "~당신의 무릎 위에 고양이~");
printf("%s\n" , str1);
return 0;
}
코드1에는 단순히 str1에 다른 문자열을 strcat() 함수를 사용하여 추가한다. str1는 문자열을 추가할 수 있도록 여분에 배열 크기를 할당하고 있다. 리터럴 문자열에 문자열을 추가하지 않도록 주의한다.
이러한 처리를 위해서는 문자열의 문자수가 중요하다. 함수가 항상 문자열의 수를 파악할 수 있는 것은 아니다. 동적으로 문자열 포인터에서 문자 수를 확인하고, malloc() 함수에서 힙을 할당하여, 이에 문자열을 편집하는 처리를 할 수 있을 것이다. 문자수를 확인하려면 배열의 처음부터 조사하여 NULL 즉 0의 값을 발견 할 때까지 계산하여 얻을 수 있지만, 이 처리도 표준 함수가 지원해주고 있다. 문자수를 얻으려면 strlen() 함수를 사용한다.
strlen() 함수
size_t strlen( const char *string );
string의 문자수를 확인하고 싶은 NULL로 끝나는 문자 배열의 포인터를 지정한다. strlen () 함수는 string의 문자 수를 반환한다. 이 문자 수에는 맨 끝의 NULL 문자는 포함되지 않으므로 주의하자.
코드2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char *str1 = "Kitty on your lap ";
const char *str2 = "~당신의 무릎 위에 고양이~";
char *str3 = (char*)malloc(strlen(str1) + strlen(str2) + 1);
*str3 = 0;
strcat(str3 , str1);
strcat(str3 , str2);
printf("%s\n" , str3);
free(str3);
return 0;
}
코드2에는 strlen() 함수를 사용하여 문자열에 대한 포인터 str1과 str2에서 문자 수를 조사하고 있다. 문자 수는 malloc() 함수에 의해 메모리 할당에 사용되고, 동적으로 문자의 가산 처리를 실시하고 있다. str3는 str1과 str2의 문자열을 strcat()에 추가하기 위해 충분한 메모리 공간이 할당한다. 이러한 처리를 실시하면, 매개 변수로 받은 문자열에 대한 포인터 등, 함수의 작성 단계에서 문자를 정적으로 파악할 수 없는 경우에도 올바른 문자열 처리를 할 수 있다.
코드2의 중간에 str3에 문자열 str1을 추가하기 위해 str3의 앞 요소에 일부러 0 (NULL 문자)를 대입하고, strcat() 함수를 사용하고 있는데, 이것은 제대로 된 방법이라고 할 수 없다. 첫번째 strcat(str3, str1)는 단순히 str3에 str1을 복사하고 있을 뿐이다. 문자열을 복사할 경우 strcpy() 함수를 사용하는 것이 좋다.
strcpy() 함수
char *strcpy( char *string1, const char *string2 );
이 함수는 단순히 string1에 string2를 NULL 문자를 포함하여 복사한다. 그 이외의 동작은 기본적으로 strcat() 함수와 같고, 함수는 대상에 문자열 string1을 반환한다. string1에 할당된 메모리 공간이 부족하거나, string1과 string2가 겹쳐져 있는 경우의 동작은 정의되지 않는다.
그런데 이러한 문자열 편집 처리를 프로그램을 수행할 경우, if문으로 문자열 비교가 요구될지도 모른다. 문자열에 대한 포인터가 동일한지 여부를 조사하는 것은 간단하지만, 두개의 NULL로 끝나는 문자 배열이 완전히 똑같은 내용의 문자 배열을 확인하려면 strcmp() 함수를 사용한다.
strcmp() 함수
int strcmp( const char *string1, const char *string2 );
string1과 string2는 비교할 NULL으로 끝나는 문자열에 대한 포인터를 지정한다. 함수의 반환 값이 0이면 두 문자열이 같은 문자열임을 나타낸다. 음수를 돌려 주었을 경우는 사전 순으로 string1이 string2보다 작고, 양수를 반환하면 string1이 string2보다 크다는 것을 나타낸다.
코드3
#include <stdio.h>
#include <string.h>
int main() {
char str1[255], str2[255];
printf("문자열 2개를 입력하십시오.>");
scanf("%s %s", str1, str2);
if (strcmp(str1 , str2) == 0)
printf("%s와 %s는 같다.\n" , str1 , str2);
else if(strcmp(str1 , str2) < 0)
printf("%s와 %s보다 작다.\n" , str1 , str2);
else
printf("%s는 %s보다 크다.\n" , str1 , str2);
return 0;
}
코드3은 커멘드 라인 인수로 2개의 문자열을 입력한다. 그러면 프로그램은 입력된 문자열을 strcmp() 함수로 비교하고, 그 결과를 표준 출력에 표시한다. 문자열을 사전 순으로 정렬하거나, 동일한 문자열 여부를 확인 할때에 strcmp()는 유용하다.
여기까지 소개한 문자열 관련 함수는 간단한 문자열의 제어에 자주 사용되는 함수이지만, 복잡한 처리가되면 이것만으로는 실현될 수 없다. 예를 들어 숫자 형식의 변수를 문자열로 변환하여 다른 문자열에 추가하려면 매우 시간이 걸릴 것이다. 이를 쉽게 달성할 방법이 있다면 편리하지만, 문자열 관련 함수에 그런 것은 없다.
그러나, 우리는 이미 고급 문자열 변환을 수행하는 함수를 사용하고 있다. printf() 함수와 fprintf() 함수이다. 이것들은 서식 제어 문자열과 가변 개수의 옵션 인수를 사용하여 숫자나 부동소수, 문자 등을 하나의 문자열로 출력할 수 있었다. printf()와 fprintf() 함수는 문자열을 스트림에 출력했지만, 메모리 버퍼에 출력할 수 있으면, 문자열로 변환을 printf() 함수처럼 할 수 있다는 것이다. 이를 실현하는 함수가 sprintf () 함수이다.
sprintf() 함수
int sprintf( char *buffer, const char *format [, argument] ... );
buffer에는 문자열의 출력이 되는 버퍼의 포인터를, format에는 서식 제어 문자열을, argument는 옵션 인수를 지정한다. format과 argument 내용은 printf() 함수와 완전히 동일하다. 첫번째 인수에 출력의 포인터를 지정하는 점에서 printf() 함수와 다르다. 이 함수를 사용하면, 본래 printf() 함수가 표준 출력에 표시하게 될 문자열을 버퍼에, 즉 충분한 저장 공간이 할당된 문자 배열로 출력할 수 있다.
sprintf() 함수가 있기 때문에 예상할 수 있지만 sscanf() 함수도 존재한다. 이 함수는 버퍼에서 입력을 수행한다.
sscanf() 함수
int sscanf( const char *buffer, const char *format [, argument ] ... );
이 함수도 buffer에 버퍼에 대한 포인터를 지정한다. format에는 서식 제어 문자열, argument에는 옵션 인수를 지정한다. 역시, sprintf()와 동일하게 첫번째 인수에 버퍼에 대한 포인터를 지정하는 점을 제외하고 scanf() 함수와 동일하다.
코드4
#include <stdio.h>
int main() {
char str[256];
sprintf(str , "오늘은 %d년 %d월 %d일이다." , 2017 , 11 , 26);
printf("%s\n" , str);
return 0;
}
코드4는 sprintf() 함수를 사용하여, 지금까지 표준 출력에 문자열을 표시해 온 방법과 동일한 방법으로 문자열 편집을 실시하고 있다. 이 방법이라면 수치나 다른 문자열 등을 일괄적으로 문자열로 변환할 수 있다. 마찬가지로, 문자열을 숫자 등으로 변환하는 경우는 sscanf() 함수를 사용하기만 하면 된다.