ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 문자열 파싱 연습하기(feat. strtok_s)
    개발/C·C++ 2024. 7. 4. 16:39

    C 표준 함수인 strtok_s는 원본 배열에 건드리기 때문에 const char*을 인수로 받지 않는다. 구분자는 하나의 문자열에 넣어주면 되고 내부 상태값을 유지하기 위해 외부에서 선언한 포인터 변수의 주소를 전달하면 된다. 여기서 말하는 내부 상태 값은 원본 문자열을 검색할 때 다음의 시작 주소다. strtok_s과 동일한 로직을 가지고 사용법도 동일한 MyStrTok을 구현해보자.

     

    #include <stdio.h>
    
    char* MyStrTok(char* string, const char* delimeter, char** context);
    bool IsDelimiter(char ch, const char* delimiter);
    
    int main() {
    	char buffer_string[64] = { "data = x + y;\nresult = a * b;" };
    	const char* delimiter = " +*;\n=";
    	char* token_ptr = nullptr;
    	char* context = nullptr;
    
    	token_ptr = MyStrTok(buffer_string, delimiter, &context);
    
    	while(nullptr != token_ptr) {
    		puts(token_ptr);
    		token_ptr = MyStrTok(nullptr, delimiter, &context);
    	}
    }
    
    bool IsDelimiter(char ch, const char* delimiter)
    {
    	while (*delimiter) {
    		if (*delimiter == ch) {
    			return true;
    		}
    		++delimiter;
    	}
    
    	return false;
    }
    
    /*
    * - context: 다음 검색을 위한 내부 상태
    * - 파싱을 완료할 때마다 해당 토큰의 끝 위치 다음을 0으로 초기화
    *	-> 시작 주소에서부터 끝까지 문자열을 읽을 수 있다
    */
    char* MyStrTok(char* string, const char* delimiter, char** context)
    {
    	// 두 번째 호출부터는 context를 사용한다
    	// 직전 작업에서 *context에는 작업할 문자열의 시작 주소가 들어가야 한다
    	// 즉, 더 이상 다음 작업을 할 필요 없다면 nullptr으로 초기화해야 한다
    	char* start = string ? string : *context;
    
    	// 로직의 탈출 조건
    	if (nullptr == start) {
    		return nullptr;
    	}
    
    	// 구분자를 건너뛴다
    	// 반복문이 끝나면 \0을 만났거나 구분자가 아닌 문자가 등장
    	while (*start && IsDelimiter(*start, delimiter)) {
    		++start;
    	}
    
    	// 문자열의 끝을 만났다면 진행하지 않는다
    	if ('\0' == *start) {
    		*context = nullptr;
    		return nullptr;
    	}
    
    	// 다음 구분자때까지 순환하기 위해 시작 주소를 저장	
    	char* token = start;
    
    	// 구분자를 만나면 반복문을 탈출한다
    	// find the end of the token
    	while (*start && !IsDelimiter(*start, delimiter)) {
    		++start;
    	}
    
    	// 문자열의 끝을 만났다면 진행하지 않는다
    	if ('\0' == *start) {
    		*context = nullptr;
    	}
    	else {
    		// 현재 위치의 구분자를 '\0'으로 만든다
    		*start = '\0';
    		// 다음 작업의 시작 주소
    		*context = start + 1;
    	}
    
    	return token;
    }

     

    댓글

Designed by Tistory.