[C 프로그래밍] 문자열함수 strtok(), strchr(), strstr()
네트웍 프로그래밍을 진행하기전에 기본적인 파일입출력 및 문자열함수 사용법을 알아 보도록 하겠습니다. 파일 입출력함수는 파일시스템과 관련이 있는관계로 이번시간에는 친숙한(??) 문자열 함수부터 시작해 보겠습니다.
문자열은 C 언어에서 쌍따옴표("")로 묶여있는 자료형을 말합니다. 즉, "Hello" 나 "test" 등과 같은것 입니다. 이런 문자열을 다룰수 있는 함수는 생각보다 종류도 많고 사용하기가 약간은 까다로운 것도 있습니다. 잘 알고있고 많이 사용되는 함수로는 문자열의 길이를 구해주는 strlen(...) , 두개의 문자열이 같은지 비교해주는 strcmp(...) , 그리고 문자열 끝에 새로운 문자열을 추가해주는 strcat(...) 등이 있습니다.
이 외에도 꼭 필요하지만 많이 알려지지 않은 문자열 함수로는 strtok(...), strstr(...), strchr(...) 등이 있습니다. 특히 strtok(...) 는 fgets(...) 함수와 적절히 사용되면 기본적인 Parser 를 구현하는데 유용하게 사용될수 있습니다. 그럼 지금부터 하나씩 예제를 통해 알아보도록 하겠습니다. (문자열 관련 함수는 /usr/include/string.h 에 선언되어 있습니다. 그러므로 문자열 함수를 사용할때에는 반드시 소스에서 #include <string.h> 를 포함시키셔야 합니다)
[1] 문자열에서 특정문자의 시작 위치를 알려주는 strchr()
문자열에서 특정문자의 시작 위치를 알려주는 strchr(...) 함수의 선언은 string.h 에 다음과 같이 선언되어 있습니다.
char* strchr(const char* s, int c);
선언에서 보듯이 문자열 s 에서 특정문자(-문자열이 아닙니다-) c 가 있는 첫번째 위치의 포인터를 반환해줍니다. 만약 문자열 s 에서 찾는 문자 c 가 없으면 NULL 을 반환합니다. 첫번째 위치 포인터라는 것이 무엇인지 예제를 통해 알아보도록 하겠습니다..
-------------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char* ret = NULL;
const char* s = "This is a test program !";
ret = strchr(s, 'i');
if ( ret == NULL )
printf("Not Found...");
else
printf("%s\n", ret);
}
-------------------------------------------------------------------------------------
[그림 1]
[그림 1] 에서 보듯이 전체문자열 "This is a test program !" 에서 "is is a test program !" 만이 출력되었습니다. 소스에서 문자열 s 에는 두개의 문자 'i' 가 있지만 strchr(s, 'i') 로 인해 문자 'i' 가 시작되는 첫번째 위치의 포인터가 반환되었습니다. 즉, 변수 ret 는 "is is a test program !" 를 가리키는 포인터 입니다.
만약 위 소스에서 strchr(s, 'x'); 라고 한다면 반환값 ret 는 NULL 이 됩니다. (백문이 불여일타 입니다..직접 확인해보시기 바랍니다..^^)
[2] 문자열에서 특정 문자열의 시작위치를 알려주는 strstr()
문자열에서 특정문자열의 시작 위치를 알려주는 strshr(...) 함수의 선언은 string.h 에 다음과 같이 선언되어 있습니다.
char* strstr(const char* a, const char* b);
선언에서 보듯이 문자열 a 에서 특정문자열 b 가 있는 첫번째 위치의 포인터를 반환해줍니다. 만약 문자열 a 에서 찾는 문자열 b 가 없으면 NULL 을 반환합니다. 이번에도 예제를 통해 알아보도록 하겠습니다..
-------------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char* ret = NULL;
const char* a = "This is a test program !";
ret = strstr(a, "test");
if ( ret == NULL )
printf("Not Found...");
else
printf("%s\n", ret);
}
-------------------------------------------------------------------------------------
[그림 2]
[그림 2] 에서 보듯이 전체문자열 "This is a test program !" 에서 "test program !" 만이 출력되었습니다. 소스에서 문자열 a 에서 문자열 "test" 가 있는 첫번째 위치의 포인터가 반환되었습니다. 즉, 변수 ret 는 "test program !" 를 가리키는 포인터 입니다.
만약 위 소스에서 strshr(s, "hello"); 라고 한다면 반환값 ret 는 NULL 이 됩니다.
[3] 문자열에서 토큰(token)을 뽑아내는 strtok()
문자열에서 토큰(token)을 뽑아내는 strtok(...) 함수의 선언은 string.h 에 다음과 같이 선언되어 있습니다.
char* strtok(char* s1, char* s2);
토큰(token) 이라는 것은 특정한 구분자로 분리되는 문장구성요소입니다. (예를들어 "Hello, World" 라는 문자열이 있을때 구분자를 콤마로 정의한다면 토큰은 Hello 와 World 두개입니다.)
선언에서 보듯이 s2 문자열에 있는 구분자로 s1 믄자열을 토큰(token) 단위로 나누어 주는 함수입니다. 더 이상의 토큰이 없다면 반환값으로 NULL 을 리턴합니다. 그러나 strtok(...) 한번 수행으로 하나의 토큰만이 추출되기 때문에 계속해서 strtok(...) 를 호출해야 합니다. 즉, 처음 수행할때 s1에 구분자로 나눌 문자열을 지정하고 두번째 수행부터는 s1에 NULL값을 넣음으로써 계속해서 구분자로 나누는 작업을 할 수 있게 됩니다. 이번에도 예제를 통해 알아보도록 하겠습니다..
-------------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char* token = NULL;
char s1[] = "This is a test\tprogram !\n";
char s2[] = " ,\t\n";
token = strtok(s1, s2);
while ( token != NULL )
{
printf("token = %s\n", token);
token = strtok(NULL, s2);
}
}
-------------------------------------------------------------------------------------
[그림 3]
[그림 3] 에서 보듯이 원본문자열 "This is a test\tprogram !\n"; 이 구분자 " ,\t\n" 으로 구분이되었습니다. 즉, 구분자로 공백( ), 콤마(,) , 탭(\t) 또는 개행문자(\n) 를 사용하여 원본문자열을 토큰으로 분리했습니다. 이렇듯 strtok(...)는 문자열에서 적당한 분리자를 통해 원하는 문자열을 토큰단위로 뽑아낼 수 있습니다.
테스트를 위해 구분자에서 공백과 콤마를 제외해 보죠..이제 구분자는 탭(\t)과 개행문자(\n) 입니다. 변경된 소스는 다음과 같습니다..
-------------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char* token = NULL;
char s1[] = "This is a test\tprogram !\n";
char s2[] = "\t\n"; // 구분자에서 공백과 컴마를 제외함.
token = strtok(s1, s2);
while ( token != NULL )
{
printf("token = %s\n", token);
token = strtok(NULL, s2);
}
}
-------------------------------------------------------------------------------------
[그림 4]
[그림 4] 처럼 토큰은 2개입니다. 즉, 탭과 개행문자에 의해서만 구분되는 "This is a test" 와 "program !" 입니다.
그러나 strtok(...) 를 사용하실때는 반드시 기억해야 할것이 있습니다. strtok(...) 를 사용할때마다 원본문자열이 변한다는 것입니다. 즉, strtok(...) 를 사용하면 원본문자열 (-s1-)에 무조건 변경이 가해진다는 것입니다. 위의 소스에서 원본문자열을 다음과 같이 변경하면 실행시 에러가 납니다. 왜냐하면 원본문자열이 변경되어야 하는데 변경될 공간이 없기 때문입니다..(직접 한번 해보세요 )
char* s1 = "This is a test\tprogram !\n"; [Error]
char s1[] = "This is a test\tprogram !\n"; [OK]
이상으로 문자열 함수를 마치겠습니다..프로그래밍에는 왕도가 없다고 합니다..귀찮고 힘들어도 직접 한번 타이핑해보고 에러를 잡으면서 실력이 많이 향상되리라 봅니다.