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;
}