일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 파일입출력
- 코틀린
- 문자열파싱
- Mat 변수
- vs code
- background service
- FTP
- socket
- dictionary
- vscode
- 딕셔너리
- 문자열다루기
- InfluxDBClient
- 진수변환기
- 소켓프로그래밍
- 메모리반환
- Linked List
- Android
- Kotlin
- 자료구조
- UI
- 문자열 다루기
- ws2_32.lib
- InfluxDB
- foreground service
- 콘솔 키보드 이벤트
- 연결리스트
- C
- flutter
- dart
- Today
- Total
무슨 일로 C 하셨습니까?
[C] 문자열 다루기::문자열 파싱 본문
솔직히 문자열 다루는 항목에선 가장 고급기술이 아닐까 싶다.
물론 이 기능을 하는 함수도 <String.h>에 존재하지만
직접 구현해 볼것이다.
간단히 구상해보면
기본 문자열(Source)에서 특정 구분자[문자열/문자](separator)를 기준으로 나누어서 각 문자열들을 배열의 형태로 반환시키도록 할 것이다.
함수 기능 | 문자열을 특정 문자열(문자)을 기준으로 나눔 |
입력값 | source(문자열), separator(문자열) |
반환값 | 문자열(Token) 배열 |
나누어진 여러 문자열들 |
char** g(char* Src, char* Sep) {
return NULL;
}
( Src : Source / Sep : Separator -> 구분자 )
함수를 선언해준다.
이전에 만든 함수를 통해 Source에서 Separator가 얼마나 일치하고 어디에서 일치하는지 파악한 후 반환될 배열을 만들어 주는데
char** g(char* Src, char* Sep) {
int* Finder = StringFinder(Src, Sep);
int ResultSize = 0;
char** result = (char**)malloc(sizeof(char*) * ResultSize);
for (int i = 1; i <= Finder[0]; i++) {
// Separator 사이의 문자열이 존재할 때
if (1) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = "";
}
}
free(Finder);
return result;
}
문자열 검색 때와 마찬가지로 할당해야 할 배열의 메모리 크기를 모르기 때문에 realloc()을 사용하도록 한다.
(동적 할당을 사용한 배열을 꼼꼼히 반환시켜주자)
Token 배열에 문자열을 추가할 때 고려해야할 사항은
- 구분자가 맨 앞에 있는 경우 → 첫 번째 Token은 널(NULL)값
- 구분자가 맨 뒤에 있는 경우 → 마지막 Token은 널(NULL)값
- 두 구분자 사이의 거리가 없는 경우 → 두 구분자 사이의 Token은 널(NULL)값
으로 각각의 경우를 한번에 처리하기 위해서 Token의 길이가 0일때는 무시해주면 될 것 같다.
int SrcLength = StringLength(Src);
int SepLegnth = StringLength(Sep);
int* Finder = StringFinder(Src, Sep);
int ResultSize = 0;
int StringLength = 0;
char** result = (char**)malloc(sizeof(char*) * ResultSize);
int StartIdx = 0;
int EndIdx = 0;
for (int i = 1; i <= Finder[0]; i++) {
EndIdx = Finder[i];
StringLength = EndIdx - StartIdx;
// Seperator 사이에 문자열이 존재할 때
if (StringLength > 0) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = StringAdd3("", Src + StartIdx, StringLength);
}
StartIdx = EndIdx + SepLegnth;
}
(저 가독성 떨어지는 문자열 합성 함수를 어서 처리해 주어야 할 것 같다.)
몇 가지 변수를 더 선언해 주고 로직을 추가했다.
이때 주의할 점은 구분자는 Token에 포함되지 않기 떄문에 구분자의 길이도 고려해 주어야한다.
두 구분자 사이의 거리 (구분자의 문자열 길이를 제외한) 가 존재한다면 그 길이 만큼이 Token이 되는 방식이다.
(문자열의 시작부터 구분자로 인해 구분되었다고 생각하고 고려해 주어야한다.)
그리고 그 다음 구분자로 하나씩 넘어가며 Token을 만들어서 배열에 추가해준다.
if (StartIdx + SepLegnth < SrcLength) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = StringAdd3("", Src + StartIdx + SepLegnth, SrcLength - (StartIdx + SepLegnth));
}
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = NULL;
마지막으로 마지막 구분자 뒤의 Token을 추가해 주고, 배열의 끝을 알리는 NULL까지 처리해주면 되겠다.
이름을 붙혀준 함수의 최종 코드는 다음과 같다.
char** StringParser(char* Src, char* Sep) {
int SrcLength = StringLength(Src);
int SepLegnth = StringLength(Sep);
int* Finder = StringFinder(Src, Sep);
int ResultSize = 0;
int StringLength = 0;
char** result = (char**)malloc(sizeof(char*) * ResultSize);
int StartIdx = 0;
int EndIdx = 0;
for (int i = 1; i <= Finder[0]; i++) {
EndIdx = Finder[i];
StringLength = EndIdx - StartIdx;
if (StringLength == 0) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = StringAdd1("", "");
}
// 시작점이 다음점과 일치하는 경우
if (StringLength > 0) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = StringAdd3("", Src + StartIdx, StringLength);
}
StartIdx = EndIdx + SepLegnth;
}
if (StartIdx == SrcLength) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = StringAdd1("", "");
}
else if (StartIdx < SrcLength) {
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = StringAdd1("", Src + StartIdx);
}
result = (char**)realloc(result, sizeof(char*) * ++ResultSize);
result[ResultSize - 1] = NULL;
free(Finder);
return result;
}
이 함수가 정상 작동하는지는 직접 테스트해보길 바란다.
함수의 사용은 포인터가 NULL을 가리킬 때까지 반복해보면 되겠다.
char** ptr=StringParser("asdvasvasdfasdfsdfasdvzxcv","x");
for (int i = 0; ptr[i] != NULL; i++) {
printf("%d is : %s\n", i, ptr[i]);
free(ptr[i]);
}
free(ptr);
메모리 반환은 필수
'C - 이걸 굳이? > 유틸리티' 카테고리의 다른 글
[C] 자료구조::연결 리스트 (LINKED LIST) _ 1 (0) | 2020.10.07 |
---|---|
[C] 문자열 다루기::문자열 치환 + α (0) | 2020.09.28 |
[C] 문자열 다루기::문자열 검색 (0) | 2020.09.04 |
[C] 문자열 다루기::문자열 비교 (0) | 2020.09.02 |
[C] 문자열 다루기::문자열 합성 (0) | 2020.08.25 |