C언어로 코딩하실 때, 파싱 문제를 만날 때 가장 많이 쓰는 함수는 strtok입니다. 물론, 저는 strchr 조합을 더 많이 쓰긴 합니다만. 익혀두면 편한 쪽은 오늘 소개하는 함수입니다. 함수 원형을 소개하지 않겠습니다. 거의 정석처럼 사용되는 패턴 정도만 소개하도록 하겠습니다.

 

 


 먼저 '#'을 구분자로 끊어내 봅시다.

 

 

 보시면 strtok의 1번째 인자에는 parsing을 할, 대상체가 들어가 있습니다. 예를 들자면, "abcd#efg#hijk"와 같은 것들입니다. 그리고, 2번째 인자에는 구분자 집합이 들어가는데요. 여기에서 우리는 '#'을 기준으로 자를 것이므로 '#'만 넣었음을 알 수 있습니다.

 

 12번째 줄이 핵심인데요. strtok는 문자열의 포인터를 리턴해 줍니다. 문자열의 끝에 도달하면, NULL을 돌려주는데요. 이 때에는 다 끊어낸 경우입니다. while loop 안에, 또 strtok를 호출한다는 것을 알 수 있는데요. 이 때에는 1번째 인자로 NULL 값을 넘겨줍니다. 2번째 인자는 구분자를 넣어주고요. 그냥, LOOP 안에서 호출하는 strtok 함수의 1번째 인자에는 널 포인터를 넣어준다는 것만 기억하시면 됩니다.

 

 

 "abc#def#efgh#ijklmn#gahui"가 들어왔을 때, 출력된 결과입니다.

 

 


 그러면 "123|cho|array_list_is_good|..." 이런 식으로 문자열이 들어왔을 때, '|'을 구분자로 끊으려면 어떻게 해야 할까요? 2번째 인자를 "#"에서 "|"으로 바꾸면 됩니다. '|'만을 기준으로 끊어낸다면 말입니다.

 

 

 그러면 안에 parse 함수가 이렇게 바뀌면 됩니다.

 

 

 

 실제로 "123|cho|array_list_is_good|2" 이런 문자열이 들어왔을 때, 출력되는 값입니다. '|'를 기준으로 잘 끊어졌음을 알 수 있습니다. 만약에 데이터가 "123|cho||2" 이런 식으로 들어왔다면 어떻게 출력이 될까요?

 

 

 123, cho, 2 순으로 출력이 됩니다. 2번째 '|'과 3번째 '|' 사이는 출력되지 않았습니다. 이 점은 조심하셔야 합니다. 만약에, 123, cho, , 2 이렇게 출력하려면 어떻게 하면 좋을까요?

 

 

 strchr 함수로, 돌리면 됩니다. 이것은 해당 위치에서부터 '|'가 나오는 최초의 위치를 리턴합니다. 그 전에 NULL 문자가 나오면, NULL을 리턴합니다. 14번째 줄이 while Loop를 빠져나오는 조건입니다. 만약에, '|'가 있다면, 해당 위치를 널 문자로 임시로 채운 다음에, str+prev를 출력합니다.

 

 그리고, str[lo]의 값을 '|'로 원상 복구를 한 다음에, prev의 값을 lo+1로 바꿔버립니다.

 

 

 그러면, 빈 필드도 제대로 출력이 됩니다. 이건 그냥 팁이니 알아두시면 언젠가는 유용하게 쓰이리라 생각이 듭니다.

 

 


 마지막으로 #과 @를 구분자로 끊으려면 어떻게 해야 할까요?

 

 

 

  2번째 인자만, "#@"로 바꾸면 됩니다. 2번째 인자는 구분자 집합들을 나타내는 것인데요. '#'과 '@'가 구분자입니다. 따라서, "#@"를 넣으면 됩니다. "123@#cho@hotmail#|2"를 입력하면 다음과 같은 결과가 출력이 됩니다.

 

 

 그런데, strtok를 쓰신다면 주의하셔야 할 점이 몇 가지가 있어요. 1번째는 원본이 변형된다는 것입니다. 아실 수도 있겠지만, 구분자가 있는 곳이 0으로 덮어쓰기가 됩니다.

 

 

 

 1번째 인자가 왜 NULL이 들어갔을까? 를 생각해 보면, 당연한 이야기일지도 모릅니다. 사실 parse를 해서 끊어내는 작업을 한다면, 구분자 부분을 NULL 문자로 만들어서 처리하는 게 제일 편한 방법이거든요. 실제로 그런지 볼까요?

 

 

 저는 str[i]에 저장이 되어 있는 값을 출력하고자 합니다. 그러면 0부터 29까지 돌면서 str[i]의 값을 print 하면 될 겁니다.

 

 

 중간 중간 0이 들어가 있는 것을 알 수 있는데요. 이는 구분자를 만났을 때, 처리를 쉽게 하기 위해서, 그렇게 한다는 것을 알 수 있어요. 여기까지는 그렇게 크게 어려울 것은 없어 보입니다. 그런데, 한 가지 더 주의해야 할 것이 있습니다. 리눅스에서는 이 함수가 별도로 있습니다. strtok_r. 뒤에 _r이 붙은 버전이 있다는 것은, 이 함수가 스레드 안전하지 않다는 뜻입니다.

 

 그러면 내부적으로 공유 변수를 쓴다는 이야기인데요. 잘 생각해 보면 일리가 있는 말입니다. main 함수를 아무리 뒤져봐도, 문자열을 끊은 위치와 관련된 그 어떠한 것도 보이지 않기 때문입니다. 어떻게, 끊어낸 위치를 귀신같이 알아낼 수 있을까요? 내부에서 static이 붙은 공유 변수를 쓰면 가능합니다. 이러한 부분은 조심해야 합니다.