string.h 헤더 파일에 선언되어 있는 strtok 함수를 사용하면 문자열을 자를 수 있다.
strtok(대상문자열,기준문자);자른문자열을반환,더이상자를문자열이없으면NULL을반환
#define _CRT_SECURE_NO_WARNINGS // strtok 보안 경고로 인한 컴파일 에러 방지#include<stdio.h>#include<string.h> // strtok 함수가 선언된 헤더 파일intmain(){chars1[30]="The Little Prince";// 크기가 30인 char 타입 배열을 선언하고 문자열 할당char*ptr=strtok(s1," ");// " " 공백 문자를 기준으로 문자열을 자름, 포인터 반환while(ptr!=NULL)// 자른 문자열이 나오지 않을 때까지 반복{printf("%s\n",ptr);// 자른 문자열 출력ptr=strtok(NULL," ");// 다음 문자열을 잘라서 포인터를 반환}return0;}// The// Little// Prince
strtok 함수는 지정된 문자를 기준으로 문자열을 자른다.
즉, strtok(s1, " ");와 같이 " "(공백 문자)를 넣어주면 공백으로 구분하여 문자열을 자른다.
단, 기준 문자를 ' '로 묶으면 안 된다.
특히 strtok 함수는 잘린 문자열을 한 번에 얻을 수 없어서 while 반복문으로 문자열을 계속 자르다가 문자열이 나오지 않으면 반복문을 끝내는 방식으로 사용한다.
char*ptr=strtok(s1," ");// " " 공백 문자를 기준으로 문자열을 자름, 포인터 반환while(ptr!=NULL)// 자른 문자열이 나오지 않을 때까지 반복{printf("%s\n",ptr);// 자른 문자열 출력ptr=strtok(NULL," ");// 다음 문자열을 잘라서 포인터를 반환}
여기서 while 반복문 안의 strtok 함수는 ptr = strtok(NULL, " ");처럼 자를 문자열 부분에 NULL을 넣어준다.
자세히 설명하자면 NULL을 넣었을 때는 직전 strtok 함수에서 처리했던 문자열에서 잘린 문자열만큼 다음 문자로 이동한 뒤 다음 문자열을 자른다.
만약 ptr = strtok(ptr, " ");처럼 잘린 문자열의 포인터를 다시 넣었을 때는 다음 문자로 이동하지 못하고 처음에 나오는 문자열만 계속 자르게 된다.
TheTheTheThe...
strtok 함수를 사용할 때는 처음에만 자를 문자열을 넣어주고, 그다음부터는 NULL을 넣어준다는 점을 기억하자.
다음은 strtok 함수의 동작 순서이다.
먼저 처음 호출되는 strtok는 " "를 찾아서 NULL로 채운 뒤 문자열의 첫 부분인 "The"를 자른다.
이제 while 반복문 안의 strtok에 NULL을 넣어주어 앞에서 잘린 문자열만큼 다음 문자로 이동한다.
그리고 다시 " "를 찾아서 NULL로 채운 뒤 "Little"을 자른다.
하지만 아직 문자열 끝에 있는 NULL을 만나지 못했으므로 계속 반복한다.
다시 strtok에 NULL을 넣어주어 앞에서 잘린 문자열만큼 다음 문자로 이동한다.
이번에는 " "가 아닌 문자열 마지막의 NULL을 만났으므로 NULL을 그대로 두고 "Prince"를 반환한다.
직전 strtok에서 " "를 만나지 못했으므로 더이상 자를 문자열이 없다.
따라서 NULL을 반환하고 while 반복문을 끝낸다.
strtok 함수는 문자열을 새로 생성해서 반환하는 것이 아니라 자르는 부분을 NULL로 채운 뒤 잘린 문자열의 포인터를 반환한다.
따라서 원본 문자열의 내용을 바꾸므로 사용에 주의해야 한다.
4. 문자열 포인터 자르기
다음과 같이 문자열 포인터에 문자열 리터럴이 들어있어서 읽기 전용인 상태라면 strtok 함수를 사용할 수 없다.
char*s1="The Little Prince";// 포인터에 문자열 리터럴 "The Little Prince"의 주소 저장char*ptr=strtok(s1," ");// 실행 에러while(ptr!=NULL){printf("%s\n",ptr);ptr=strtok(NULL," ");}
문자열 포인터에 문자열 리터럴을 할당하는 대신 동적 메모리를 할당하고, 문자열을 복사하면 이 문제를 해결할 수 있다.
char*s1=malloc(sizeof(char)*30);// char 타입 30개 크기만큼 동적 메모리 할당strcpy(s1,"The Little Prince");// s1에 문자열 복사char*ptr=strtok(s1," ");// 동적 메모리에 들어있는 문자열은 자를 수 있음while(ptr!=NULL){printf("%s\n",ptr);ptr=strtok(NULL," ");}free(s1);// 동적 메모리 해제
5. 날짜와 시간값 자르기
strtok 함수는 " "뿐만 아니라 다양한 특수 문자와 알파벳 영문자를 기준으로 문자열을 자를 수 있다.
특히 기준 문자는 한 번에 여러 개를 지정할 수 있다.
다음은 연-월-일T시:분:초 형식으로 된 문자열 "2015-06-10T15:32:19"을 잘라서 "2015", "06", "10", "15", "32", "19"를 각 줄에 출력한다.
#define _CRT_SECURE_NO_WARNINGS // strtok 보안 경고로 인한 컴파일 에러 방지#include<stdio.h>#include<string.h> // strtok 함수가 선언된 헤더 파일intmain(){chars1[30]="2015-06-10T15:32:19";// 크기가 30인 char 타입 배열을 선언하고 문자열 할당char*ptr=strtok(s1,"-T:");// -, T, 콜론을 기준으로 문자열을 자름, 포인터 반환while(ptr!=NULL)// 자른 문자열이 나오지 않을 때까지 반복{printf("%s\n",ptr);// 자른 문자열 출력ptr=strtok(NULL,"-T:");// 다음 문자열을 잘라서 포인터를 반환}return0;}// 2015// 06// 10// 15// 32// 19
-, T, :(콜론)을 기준으로 문자열을 자르므로 "-T:"와 같이 기준 문자를 여러 개 넣었다.
6. 자른 문자열 보관하기
지금까지 자른 문자열을 출력만 하고 끝냈다.
하지만 실제로 프로그램을 작성할 때는 자른 문자열을 보관했다가 계속 사용하는 경우가 많다.
예를 들어 문자열을 자른 뒤 다른 코드를 실행하고, 자른 문자열을 사용한다든지 자른 문자열을 또 다른 처리에 사용한다든지 하는 상황이 생길 수 있다.
즉, 문자열을 자르는 while 반복문 안에서 모든 처리를 할 수 없는 상황에서는 자른 문자열을 보관할 필요가 없다.
이번에는 문자열 포인터 배열에 자른 문자열을 보관해 보자.
#define _CRT_SECURE_NO_WARNINGS // strtok 보안 경고로 인한 컴파일 에러 방지#include<stdio.h>#include<string.h> // strtok 함수가 선언된 헤더 파일intmain(){chars1[30]="The Little Prince";// 크기가 30인 char 타입 배열을 선언하고 문자열 할당char*sArr[10]={// 크기가 10인 문자열 포인터 배열을 선언하고 NULL로 초기화NULL,};inti=0;// 문자열 포인터 배열의 인덱스로 사용할 변수char*ptr=strtok(s1," ");// 공백 문자열을 기준으로 문자열을 자름while(ptr!=NULL)// 자른 문자열이 나오지 않을 때까지 반복{sArr[i]=ptr;// 문자열을 자른 뒤 메모리 주소를 문자열 포인터 배열에 저장i++;// 인덱스 증가ptr=strtok(NULL," ");// 다음 문자열을 잘라서 포인터를 반환}for(inti=0;i<10;i++){if(sArr[i]!=NULL)// 문자열 포인터 배열의 요소가 NULL이 아닐 때만{printf("%s\n",sArr[i]);// 문자열 포인터 배열에 인덱스로 접근하여 각 문자열 출력}}return0;}// The// Little// Prince
먼저 char *sArr[10] = {NULL,};과 같이 자른 문자열을 보관할 문자열 포인터 배열을 선언하고 NULL로 초기화했다.
여기서 NULL 뒤에 ,(콤마)를 붙여주면 배열의 모든 요소가 NULL로 초기화된다.
while 반복문 안에서는 sArr[i] = ptr;과 같이 자른 문자열의 메모리 주소를 배열에 저장하고, 배열의 인덱스를 증가시킨다.
포인터 ptr은 반복문을 반복하면서 문자열을 자를 때마다 안에 저장된 메모리 주소가 계속 바뀌므로 나중에 다시 사용할 수 없다.
하지만 예제처럼 ptr에 저장된 메모리 주소가 바뀌기 전에 다른 곳에 보관해 두면 자른 문자열을 나중에도 계속 사용할 수 있다.