83.8 심사문제 질문
, 군대에서 다시해보자님이 작성83.8 example.json 파일을 파싱하여 출력하는 문제의 통과기준이 궁금합니다.
2가지 코드로 심사를 진행해보았습니다.
1번째 코드는 토큰을 하나씩 출력하는 방식이고,
2번째 코드는 키값을 바탕으로 값을 불러오는 함수까지 구현한 코드입니다.
결과적으로 2번째 코드만 심사를 통과하였습니다(예제에 한해서 출력값은 같습니다.)
이것이 다른 예제의 경우, 예를 들어 Actors의 배열 요소가 추가된다던가 등등의 변수가 있을 때까지 고려해야해서 생기는 문제인지
아니면 다른 원인이 있어서 정답의 유무가 갈리는지 궁금합니다.
아래는 코드입니다.
먼저 그냥 하나씩 출력한 1번째 코드입니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 토큰 종류 열거형
typedef enum _TOKEN_TYPE{
TOKEN_STRING, // 문자열 토큰
TOKEN_NUMBER, // 숫자 토큰
} TOKEN_TYPE;
// 토큰 구조체
typedef struct _TOKEN{
TOKEN_TYPE type; // 토큰 종류
union{ // 두 종류 중 하나만 저장할 것이므로 공용체 사용
char* string; // 문자열 포인터
double number; // 실수형 숫자
// JSON 문서이 있는 숫자가 정수여도 double에 저장. 실수는 그대로 쓰고
// 정수는 값 받아와서 정수로 형변환하기
};
bool isArray; // 현재 토큰이 배열인지 표시
} TOKEN;
#define TOKEN_COUNT 20 // 토큰 최대 개수 20개로 설정
// JSON 구조체
typedef struct _JSON{
TOKEN tokens[TOKEN_COUNT]; // 토큰 배열
} JSON;
char *readFile(char *filename,int *readSize) // 파일을 읽어서 내용을 반환하는 함수
{
FILE *fp = fopen(filename, "rb");
if(fp == NULL)
return NULL;
int size;
char* buffer;
// 파일 크기 구하기
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
// 파일 크기 + NULL 공간만큼 메모리 할당하고 0으로 초기화
buffer = malloc(size + 1);
memset(buffer,0,size+1);
// 파일 내용 읽기
if(fread(buffer, size, 1, fp)<1)
{
*readSize = 0;
free(buffer);
fclose(fp);
return NULL;
}
// 파일 크기를 넘겨줌
*readSize = size;
fclose(fp);
return buffer;
}
void parseJSON(char *doc,int size, JSON *json) // json 파싱 함수
{
int tokenIndex = 0; // 토큰 인덱스
int pos = 0; // 문자 검색 위치를 저장하는 변수
if(doc[pos]!='{') // 문서의 시작이 {인지 검사
return;
pos++; // 다음 문자로
while(pos<size) // 문서 크기만큼 반복
{
switch (doc[pos]) // 문자의 종류에 따라 분기
{
case '"': // 문자가 "이면 문자열
{
// 문자열의 시작 위치를 구함. 맨 앞의 "를 제외하기 위해 +1
char *begin = doc + pos + 1 ;
// 문자열의 끝 위치(다음"위치)를 구함
char *end = strchr(begin,'"');
if(end == NULL)
break; // "가 없으면 잘못된 문법이니 반복 종료(switch만 끝남. while은 계속)
int stringLength = end - begin; // 문자열 실제 길이는 끝위치 - 시작위치
// 토큰 배열에 문자열 저장
// 토큰 종류는 문자열
json->tokens[tokenIndex].type = TOKEN_STRING;
// 문자열 길이 + NULL 공간만큼 메모리 할당 + 초기화(쓰레기값 치우기)
json->tokens[tokenIndex].string = malloc(stringLength+1);
memset(json->tokens[tokenIndex].string, 0,stringLength+1);
// 문서에서 문자열을 토큰에 저장
// 문자열 시작 위치에서 문자열 길이만큼만 복사
memcpy(json->tokens[tokenIndex].string,begin,stringLength);
// strcpy와는 다르게 3번째 인자로 사이즈를 넣어줘야함
// strcpy는 NULL값 전까지 다 가져오느라 사이즈조절해서 못가져옴. 그래서 memcpy 쓰는 것
// 토큰 인덱스 증가
tokenIndex++;
pos = pos + stringLength + 1;
// 현재 위치 + 문자열 길이 + "(+1)
}
break;
case '[': // 문자가 [이면 배열
{
pos++; // 다음 문자로
while (doc[pos]!=']') // 닫는 ]가 나오면 반복 종료
{
// 여기서는 문자열 배열만 처리
if (doc[pos]=='"')
{
// 문자열 시작 위치를 구함. 맨 앞 "제외
char *begin = pos + doc + 1;
// 문자열 끝 위치를 구함
char *end = strchr(begin, '"');
if(end==NULL)//"가 없으면 잘못된 문법이니 반복 종료
break;
int stringLength = end - begin; //문자열의 실제 길이 = 끝위치 - 시작위치
// 토큰 배열에 문자열 저장
// 토큰 종류는 문자열
json->tokens[tokenIndex].type = TOKEN_STRING;
// 문자열 길이 + NULL값으로 메모리 할당
json->tokens[tokenIndex].string = malloc(stringLength+1);
memset(json->tokens[tokenIndex].string, 0 , stringLength+1);
// 현재 문자열은 배열의 요소
// 이거 쓰는 이유 : 문자열이랑 문자열 배열에서 저장하는건 똑같음.
// 배열이라는것을 구분하려면 이렇게 하는 것
json->tokens[tokenIndex].isArray = true;
memcpy(json->tokens[tokenIndex].string, begin, stringLength);
tokenIndex++;
pos = pos + stringLength + 1;
}
pos++; //다음 문자로
}
}
break;
// JSON 문서는 텍스트 문서이므로 안에 저장된 숫자는 사람이 보기에는 숫자이지만 실제로는 문자열입니다.
// 따라서 case '0':과 같이 숫자를 문자로 처리해야 합니다
// 숫자가 음수일 수도 있으므로 case '-':와 같이 -도 함께 처리해줍니다.
case '0':case '1':case '2':case '3':case '4':case '5':case '6':
case '7':case '8':case '9':case '-':
// 문자가 숫자이거나 음수인 경우
{
// 문자열의 시작 위치를 구함
char *begin = doc + pos;
char *end;
char *buffer;
// 문자열의 끝 위치를 구함 ,가 나오거나 }가 나오거나.
end = strchr(begin,',');
if(end == NULL)
{
end = strchr(begin,'}');
if ( end == NULL)
break; // 둘다 없으면 문법이 틀렸으니 반복종료
}
// 문자열의 실제 길이는 끝 위치 - 시작 위치
int stringLength = end - begin;
// 문자열 길이 + NULL 공간만큼 메모리 할당
buffer = malloc(stringLength + 1);
memset(buffer, 0, sizeof(stringLength+1));
// 문서에서 문자열을 버퍼에 저장
memcpy(buffer, begin, stringLength);
// 토큰 종류는 숫자
json->tokens[tokenIndex].type = TOKEN_NUMBER;
// 문자열을 숫자로 변환하여 토큰에 저장
json->tokens[tokenIndex].number = atof(buffer);
// 버퍼 해제
free(buffer);
// 토큰 인덱스 증가
tokenIndex++;
// 현재 위치 + 문자열 길이 + 1(,또는})
pos = pos + stringLength + 1;
}
break;
} // switch문 끝
pos++; // 다음 문자로
} // while문 끝
} // 함수 끝
void freeJSON(JSON *json) // json 해제 함수
{
for (int i = 0; i< TOKEN_COUNT; i++) // 토큰 개수만큼 반복
{
if(json->tokens[i].type == TOKEN_STRING) // 토큰 종류가 문자열이면
free(json->tokens[i].string); // 동적 메모리 해제
}
}
int main()
{
int size; // 문서 크기
char *doc = readFile("example.json", &size);
// 파일에서 JSON 문서를 읽음, 문서 크기를 구함
if ( doc == NULL)
return -1;
JSON json = {0,}; // JSON 구조체 변수 선언 및 초기화
parseJSON(doc, size, &json); // JSON 문서 파싱
printf("Title: %s\n", json.tokens[1].string); // 토큰에 저장된 문자열 출력(Title)
printf("Year: %d\n", (int)json.tokens[3].number); // 토큰에 저장된 숫자 출력(Year)
printf("Runtime: %d\n", (int)json.tokens[5].number); // 토큰에 저장된 숫자 출력(Runtime)
printf("Genre: %s\n", json.tokens[7].string); // 토큰에 저장된 문자열 출력(Genre)
printf("Director: %s\n", json.tokens[9].string); // 토큰에 저장된 문자열 출력(Director)
printf("Actors:\n");
printf(" %s\n", json.tokens[11].string); // 토큰에 저장된 문자열 출력(Actors 배열의 요소)
printf(" %s\n", json.tokens[12].string); // 토큰에 저장된 문자열 출력(Actors 배열의 요소)
printf(" %s\n", json.tokens[13].string); // 토큰에 저장된 문자열 출력(Actors 배열의 요소)
printf(" %s\n", json.tokens[14].string); // 토큰에 저장된 문자열 출력(Actors 배열의 요소)
printf(" %s\n", json.tokens[15].string); // 토큰에 저장된 문자열 출력(Actors 배열의 요소)
printf("imdbRating: %f\n", json.tokens[17].number); // 토큰에 저장된 숫자 출력(imdbRating)
freeJSON(&json); // json 안에 할당된 동적 메모리 해제
free(doc); // 문서 동적 메모리 해제
return 0;
}
이어서 키값으로 값을 불러오는 함수를 구현한
2번째 코드입니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 토큰 종류 열거형
typedef enum _TOKEN_TYPE{
TOKEN_STRING, // 문자열 토큰
TOKEN_NUMBER, // 숫자 토큰
} TOKEN_TYPE;
// 토큰 구조체
typedef struct _TOKEN{
TOKEN_TYPE type; // 토큰 종류
union{ // 두 종류 중 하나만 저장할 것이므로 공용체 사용
char* string; // 문자열 포인터
double number; // 실수형 숫자
// JSON 문서이 있는 숫자가 정수여도 double에 저장. 실수는 그대로 쓰고
// 정수는 값 받아와서 정수로 형변환하기
};
bool isArray; // 현재 토큰이 배열인지 표시
} TOKEN;
#define TOKEN_COUNT 20 // 토큰 최대 개수 20개로 설정
// JSON 구조체
typedef struct _JSON{
TOKEN tokens[TOKEN_COUNT]; // 토큰 배열
} JSON;
char *readFile(char *filename,int *readSize) // 파일을 읽어서 내용을 반환하는 함수
{
FILE *fp = fopen(filename, "rb");
if(fp == NULL)
return NULL;
int size;
char* buffer;
// 파일 크기 구하기
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
// 파일 크기 + NULL 공간만큼 메모리 할당하고 0으로 초기화
buffer = malloc(size + 1);
memset(buffer,0,size+1);
// 파일 내용 읽기
if(fread(buffer, size, 1, fp)<1)
{
*readSize = 0;
free(buffer);
fclose(fp);
return NULL;
}
// 파일 크기를 넘겨줌
*readSize = size;
fclose(fp);
return buffer;
}
void parseJSON(char *doc,int size, JSON *json) // json 파싱 함수
{
int tokenIndex = 0; // 토큰 인덱스
int pos = 0; // 문자 검색 위치를 저장하는 변수
if(doc[pos]!='{') // 문서의 시작이 {인지 검사
return;
pos++; // 다음 문자로
while(pos<size) // 문서 크기만큼 반복
{
switch (doc[pos]) // 문자의 종류에 따라 분기
{
case '"': // 문자가 "이면 문자열
{
// 문자열의 시작 위치를 구함. 맨 앞의 "를 제외하기 위해 +1
char *begin = doc + pos + 1 ;
// 문자열의 끝 위치(다음"위치)를 구함
char *end = strchr(begin,'"');
if(end == NULL)
break; // "가 없으면 잘못된 문법이니 반복 종료(switch만 끝남. while은 계속)
int stringLength = end - begin; // 문자열 실제 길이는 끝위치 - 시작위치
// 토큰 배열에 문자열 저장
// 토큰 종류는 문자열
json->tokens[tokenIndex].type = TOKEN_STRING;
// 문자열 길이 + NULL 공간만큼 메모리 할당 + 초기화(쓰레기값 치우기)
json->tokens[tokenIndex].string = malloc(stringLength+1);
memset(json->tokens[tokenIndex].string, 0,stringLength+1);
// 문서에서 문자열을 토큰에 저장
// 문자열 시작 위치에서 문자열 길이만큼만 복사
memcpy(json->tokens[tokenIndex].string,begin,stringLength);
// strcpy와는 다르게 3번째 인자로 사이즈를 넣어줘야함
// strcpy는 NULL값 전까지 다 가져오느라 사이즈조절해서 못가져옴. 그래서 memcpy 쓰는 것
// 토큰 인덱스 증가
tokenIndex++;
pos = pos + stringLength + 1;
// 현재 위치 + 문자열 길이 + "(+1)
}
break;
case '[': // 문자가 [이면 배열
{
pos++; // 다음 문자로
while (doc[pos]!=']') // 닫는 ]가 나오면 반복 종료
{
// 여기서는 문자열 배열만 처리
if (doc[pos]=='"')
{
// 문자열 시작 위치를 구함. 맨 앞 "제외
char *begin = pos + doc + 1;
// 문자열 끝 위치를 구함
char *end = strchr(begin, '"');
if(end==NULL)//"가 없으면 잘못된 문법이니 반복 종료
break;
int stringLength = end - begin; //문자열의 실제 길이 = 끝위치 - 시작위치
// 토큰 배열에 문자열 저장
// 토큰 종류는 문자열
json->tokens[tokenIndex].type = TOKEN_STRING;
// 문자열 길이 + NULL값으로 메모리 할당
json->tokens[tokenIndex].string = malloc(stringLength+1);
memset(json->tokens[tokenIndex].string, 0 , stringLength+1);
// 현재 문자열은 배열의 요소
// 이거 쓰는 이유 : 문자열이랑 문자열 배열에서 저장하는건 똑같음.
// 배열이라는것을 구분하려면 이렇게 하는 것
json->tokens[tokenIndex].isArray = true;
memcpy(json->tokens[tokenIndex].string, begin, stringLength);
tokenIndex++;
pos = pos + stringLength + 1;
}
pos++; //다음 문자로
}
}
break;
// JSON 문서는 텍스트 문서이므로 안에 저장된 숫자는 사람이 보기에는 숫자이지만 실제로는 문자열입니다.
// 따라서 case '0':과 같이 숫자를 문자로 처리해야 합니다
// 숫자가 음수일 수도 있으므로 case '-':와 같이 -도 함께 처리해줍니다.
case '0':case '1':case '2':case '3':case '4':case '5':case '6':
case '7':case '8':case '9':case '-':
// 문자가 숫자이거나 음수인 경우
{
// 문자열의 시작 위치를 구함
char *begin = doc + pos;
char *end;
char *buffer;
// 문자열의 끝 위치를 구함 ,가 나오거나 }가 나오거나.
end = strchr(begin,',');
if(end == NULL)
{
end = strchr(begin,'}');
if ( end == NULL)
break; // 둘다 없으면 문법이 틀렸으니 반복종료
}
// 문자열의 실제 길이는 끝 위치 - 시작 위치
int stringLength = end - begin;
// 문자열 길이 + NULL 공간만큼 메모리 할당
buffer = malloc(stringLength + 1);
memset(buffer, 0, sizeof(stringLength+1));
// 문서에서 문자열을 버퍼에 저장
memcpy(buffer, begin, stringLength);
// 토큰 종류는 숫자
json->tokens[tokenIndex].type = TOKEN_NUMBER;
// 문자열을 숫자로 변환하여 토큰에 저장
json->tokens[tokenIndex].number = atof(buffer);
// 버퍼 해제
free(buffer);
// 토큰 인덱스 증가
tokenIndex++;
// 현재 위치 + 문자열 길이 + 1(,또는})
pos = pos + stringLength + 1;
}
break;
} // switch문 끝
pos++; // 다음 문자로
} // while문 끝
} // 함수 끝
char *getString(JSON *json,char *key) // 키에 해당하는 문자열을 가져오는 함수
{
for(int i = 0; i<TOKEN_COUNT;i++) // 토큰 개수만큼 반복
{
// 토큰 종류가 문자열이고 토큰의 문자열이 키와 일치하면
if(json->tokens[i].type==TOKEN_STRING&&
strcmp(json->tokens[i].string,key)==0)
{
// 바로 뒤의 토큰(i+1)이 문자열이면 그 토큰의 문자열 반환
if(json->tokens[i+1].type==TOKEN_STRING)
{
return json->tokens[i+1].string;
}
}
} // for문 끝
return NULL; // 키를 찾지 못하면 NULL 반환
} // 함수 끝
// 키에 해당하는 배열 중 인덱스를 지정하여 문자열을 가져오는 함수
char *getArrayString(JSON *json,char *key,int index)
{
for( int i = 0; i < TOKEN_COUNT ; i++) // 토큰 개수만큼 반복
{
// 토큰 종류가 문자열이면서 토큰의 문자열이 키와 일치한다면
if(json->tokens[i].type == TOKEN_STRING && strcmp(json->tokens[i].string, key)==0)
{
// 바로 뒤의 토큰(i+1)부터 배열의 요소
// 여기에 index를 더하면 해당 요소를 가져올 수 있습니다
// 인덱스를 지정한 토큰이 문자열이면서 배열이면
if (json->tokens[i+1+index].isArray==true && json->tokens[i+1+index].type==TOKEN_STRING)
{
return json->tokens[i+1+index].string;
}
}
}
return NULL; // 키를 못찾으면 NULL값 반환
}
// 배열의 요소를 가져오려면 요소의 개수를 알아내는 것이 좀 더 편리합니다
// 키에 해당하는 배열의 요소 개수를 구하는 함수
int getArrayCount(JSON *json, char *key)
{
for(int i = 0; i<TOKEN_COUNT; i++)
{
// 토큰 종류가 문자열이면서 문자열이 키와 일치하면
if(json->tokens[i].type==TOKEN_STRING&&strcmp(json->tokens[i].string, key)==0)
{
// 바로 뒤의 토큰(i+1)부터 isArray가 true인지 확인하며 개수 세어 반환
int j =0;
while (json->tokens[i+1+j].isArray == true)
{
j++;
}
return j;
}
}
return 0; // 키를 찾지 못하면 0을 반환
}
// 키에 해당하는 숫자를 가져오는 함수
double getNumber(JSON *json, char *key)
{
for(int i = 0; i<TOKEN_COUNT;i++)
{
if(json->tokens[i].type==TOKEN_STRING &&
strcmp(json->tokens[i].string,key)==0)
{
if(json->tokens[i+1].type==TOKEN_NUMBER)
return json->tokens[i+1].number;
}
}
return 0.0; // 키를 찾지 못했으면 0.0을 반환
}
void freeJSON(JSON *json) // json 해제 함수
{
for (int i = 0; i< TOKEN_COUNT; i++) // 토큰 개수만큼 반복
{
if(json->tokens[i].type == TOKEN_STRING) // 토큰 종류가 문자열이면
free(json->tokens[i].string); // 동적 메모리 해제
}
}
int main()
{
int size; // 문서 크기
char *doc = readFile("example.json", &size);
// 파일에서 JSON 문서를 읽음, 문서 크기를 구함
if ( doc == NULL)
return -1;
JSON json = {0,}; // JSON 구조체 변수 선언 및 초기화
parseJSON(doc, size, &json); // JSON 문서 파싱
printf("Title: %s\n", getString(&json, "Title")); // Title의 값 출력
printf("Year: %d\n", (int)getNumber(&json, "Year")); // Year의 값 출력
printf("Runtime: %d\n", (int)getNumber(&json, "Runtime")); // Runtime의 값 출력
printf("Genre: %s\n", getString(&json, "Genre")); // Genre의 값 출력
printf("Director: %s\n", getString(&json, "Director")); // Director의 값 출력
printf("Actors:\n");
int actors = getArrayCount(&json, "Actors"); // Actors 배열의 개수를 구함
for (int i = 0; i < actors; i++) // 배열의 요소 개수만큼 반복
printf(" %s\n", getArrayString(&json, "Actors", i)); // 인덱스를 지정하여 문자열을 가져옴
printf("imdbRating: %f\n", getNumber(&json, "imdbRating")); // imdbRating의 값 출력
freeJSON(&json); // json 안에 할당된 동적 메모리 해제
free(doc); // 문서 동적 메모리 해제
return 0;
}
감사합니다.
즐거운 추석연휴 되시길바랍니다~
Re: 83.8 심사문제 질문
, 군대에서 다시해보자님이 작성추가질문입니다.
심사를 통과한 2번 코드(함수구현코드)를 스스로 작성해보았는데,
세그먼트 오류가 발생하였습니다.
오류가 난 지점을 힘겹게 찾았는데 어떤 부분에서 잘못된 메모리를 접근한 것인지 아무리 고민해봐도 모르겠습니다.
혹시 이 부분도 알려주실 수 있으십니까?
문제가 생긴 부분은
아래 코드중 getStringArraySize 함수 부분입니다.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
typedef enum _TOKEN_TYPE{
TOKEN_STRING,
TOKEN_NUMBER
}TOKEN_TYPE;
typedef struct _TOKEN{
TOKEN_TYPE type;
union{
char *string;
double num;
};
bool isArray;
}TOKEN;
#define TOKEN_COUNT 20
typedef struct _JSON{
TOKEN tokens[TOKEN_COUNT];
}JSON;
char* readFile(char* filename,int *readsize)
{
FILE *fp = fopen(filename, "rb"); //r이나 rb나 둘다 작동한다
if(fp==NULL)
{
// fclose(fp); // 이러면 오류가 난다. 파일이 안열렸는데 어떻게 닫을까?
// 잘못된 메모리접근. 세그먼트 오류가 발생
return NULL;
}
fseek(fp,0,SEEK_END);
int size = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *buffer = malloc(size+1);
memset(buffer, 0, size+1);
if(fread(buffer, size, 1, fp)<1)
{
*readsize = 0;
free(buffer);
fclose(fp);
return NULL;
}
*readsize = size;
fclose(fp);
return buffer;
}
void parseFile(char* docs,int size,JSON *json)
{
int pos=0; // 포지션 저장용 변수
int tokenindex =0; // 토큰 인덱스 저장용 변수
if (docs[pos]!='{')
{
return;
}
pos++;
while (pos<size) // 사이즈만큼 다 보기
{
switch (docs[pos]) // 경우의 수 나눠서 문자열 / 숫자 / 문자열 배열 저장
{
case '"': // 문자열의 경우
{ // 중괄호 해야함. 왜냐? 새 변수 선언할꺼니까. 여기서만 써야함. + 안하면 오류남
char *begin= docs + pos + 1;
char *end = strchr(begin,'"');
if(end==NULL)
// return;
break;
int stringLength = end - begin;
json->tokens[tokenindex].type = TOKEN_STRING;
json->tokens[tokenindex].string = malloc(stringLength+1);
// NULL값이 들어갈 공간도 할당해주자
memset(json->tokens[tokenindex].string,0, stringLength+1);
memcpy(json->tokens[tokenindex].string, begin, stringLength);
tokenindex++;
pos = pos + stringLength + 1;
}
break;
case '1':case '2':case '3':case '4':case '5':case '6':case '7':
case '8':case '9':case '0':case '-':
{
char *begin = docs + pos;
char *end = strchr(begin,',');
if (end ==NULL)
{
end = strchr(begin,'}');
if(end ==NULL)
{
break;
}
}
int numberLength = end - begin;
// 일단 문자열로 받아와야하니 NULL값 공간만큼 더 받아줌.
// 문자열로 받는 이유 : json파일의 숫자는 결국 문자열임
char *buffer = malloc(numberLength+1);
memset(buffer, 0, numberLength+1);
memcpy(buffer, begin, numberLength);
json->tokens[tokenindex].type = TOKEN_NUMBER;
json->tokens[tokenindex].num = atof(buffer);
free(buffer);
tokenindex++;
pos = pos + numberLength+1;
}
break;
case '[': // 문자열 배열일 경우
{
pos++;
while(docs[pos]!=']')
{
if(docs[pos]=='"')
{
char *begin= docs + pos + 1;
char *end = strchr(begin,'"');
if(end==NULL)
break;
int stringLength = end - begin;
json->tokens[tokenindex].isArray=true;
json->tokens[tokenindex].type = TOKEN_STRING;
json->tokens[tokenindex].string = malloc(stringLength+1);
// NULL값이 들어갈 공간도 할당해주자
memset(json->tokens[tokenindex].string,0, stringLength+1);
memcpy(json->tokens[tokenindex].string, begin, stringLength);
tokenindex++;
pos = pos + stringLength + 1;
// pos는 인덱스값임. 사실상 pos+stringLength+1의 인덱스는 end인데(두번째")
// 인덱스라서 pos = end;를 하면 안됨.
}
pos++;
}
}
} // switch문 끝
pos ++;
} // while문 끝
}
char *getString(JSON *json,char *key)
{
for(int i = 0;i<TOKEN_COUNT;i++)
{
if(json->tokens[i].type==TOKEN_STRING
&&strcmp(json->tokens[i].string, key)==0)
{
if(json->tokens[i+1].type==TOKEN_STRING)
return json->tokens[i+1].string;
}
}
return NULL;
}
double getNumber(JSON *json,char *key)
{
for(int i = 0;i<TOKEN_COUNT;i++)
{
if(json->tokens[i].type==TOKEN_STRING
&&strcmp(json->tokens[i].string, key)==0)
{
if(json->tokens[i+1].type==TOKEN_NUMBER)
return json->tokens[i+1].num;
}
}
return 0.0;
}
char *getStringArray(JSON *json,char *key,int index)
{
for(int i = 0;i<TOKEN_COUNT;i++)
{
if(json->tokens[i].type==TOKEN_STRING
&&strcmp(json->tokens[i].string, key)==0)
{
if(json->tokens[i+1+index].type==TOKEN_STRING&&json->tokens[i+1+index].isArray==true)
return json->tokens[i+1+index].string;
}
}
return NULL;
}
int getStringArraySize(JSON *json,char *key)
{
int index = 0;
for(int i = 0;i<TOKEN_COUNT;i++)
{
if(json->tokens[i].type==TOKEN_STRING
&&strcmp(json->tokens[i].string, key)==0)
{
while(json->tokens[i+1+index].isArray==true)
{
index++;
}
return index; // 이거 없으면 세그먼트 오류남.
// 왜나는걸까?? <- 이 부분이 지금 보시는 코드에서 질문입니다.
}
}
return index;
}
void freeJSON(JSON *json)
{
for(int i = 0;i<TOKEN_COUNT;i++)
{
if(json->tokens[i].type==TOKEN_STRING)
free(json->tokens[i].string);
}
}
int main()
{
int size;
char *docs = readFile("example.json", &size);
if(docs==NULL)
return -1;
JSON json = {0,}; // JSON 구조체 변수 선언 및 초기화
parseFile(docs, size, &json); // JSON 문서 파싱
printf("Title: %s\n", getString(&json, "Title")); // Title의 값 출력
printf("Year: %d\n", (int)getNumber(&json, "Year")); // Year의 값 출력
printf("Runtime: %d\n", (int)getNumber(&json, "Runtime")); // Runtime의 값 출력
printf("Genre: %s\n", getString(&json, "Genre")); // Genre의 값 출력
printf("Director: %s\n", getString(&json, "Director")); // Director의 값 출력
printf("Actors:\n");
int actors = getStringArraySize(&json, "Actors"); // Actors 배열의 개수를 구함
for (int i = 0; i < actors; i++) // 배열의 요소 개수만큼 반복
printf(" %s\n", getStringArray(&json, "Actors", i)); // 인덱스를 지정하여 문자열을 가져옴
printf("imdbRating: %f\n", getNumber(&json, "imdbRating")); // imdbRating의 값 출력
freeJSON(&json); // json 안에 할당된 동적 메모리 해제
free(docs); // 문서 동적 메모리 해제
return 0;
}