2013-10-31 4 views
1

나는 다소 사소한 일로 엉망이되어 버렸습니다 ... 기본적으로 첫 번째와 마지막 것 사이의 "단어"가 데이터로 이동하고 마지막 하나는 키로 이동하기를 원합니다.문자열에서 요소 가져 오기

C-POSIX only, pls.

strtok_r을 (를) 사용 할 수있는 방법인가요? 다른 것?

char *key = NULL, *data=NULL, *save=NULL; 
char comando[1024]; 
fgets(comando, 512, stdin); 

strtok_r(comando, " ",&save); 

while(strcmp(save,"\n")){ 
    strcat(data,strtok_r(NULL," ",&save)); 
} 

key = strtok_r(NULL, "\n",&save); 

P.S : comando는 1024이므로 메모리는 문제가되지 않으며 미안보다 안전합니다. fgets는 표준 유닉스 터미널에서 char 라인 제한 때문에 512 '를 읽습니다.

+0

가 디버깅을 시도 해 봤나 : 또한, strtok_rNULL

내가 뭔가 더 좋아 할 것 반환 할 수 있습니다? 'save'가'strtok_r()'을 호출 할 때마다 가리키는 것과 같은 결과를 출력 할 것입니다. – gnobal

+0

'strtok_r '을 사용하는 것이 좋다고 생각합니까? 아니면 잘못된 것이 있습니까? – PhillipD

+0

'data'와'key'를위한 메모리를 할당해야합니다. 지금 널 포인터에 추가 중입니다. –

답변

1

코드는이 라인에 충돌합니다 :

strcat(data,strtok_r(NULL," ",&save)); 

당신이 data을위한 공간을 예약하지 때문입니다. strcatNULL 메모리 주소에 쓰려고 시도합니다.

줄의 끝을 확인하려면 save에 의존해서는 안됩니다. strtok의 맨 페이지에 따르면

saveptr 인수가 문자 strtok_r에 의해 내부적으로 을 사용 * 변수에 대한 포인터() 같은 문자열을 구문 분석 연속 통화 사이의 맥락을 유지하기 위해.

strtok_r 휴식 추상화 층의 saveptr 외부의 값에 의존, 당신은 strtoksaveptr를 사용하는 방법에 대해 아무것도 가정하지 않아야합니다. 나쁜 습관입니다.

약간 더 나은 방법은 이전 토큰에 대한 포인터를 strtok이 반환하고 현재 토큰을 가리키는 포인터를 유지하는 것입니다. strtok이 NULL을 반환하면 더 이상 토큰이 없음을 의미하고 prev은 마지막 토큰 인 key을 가리 킵니다. 내가 배열 대신 포인터로 선언 data을위한 공간을 할당

char *key = NULL, *save=NULL; 
char *prev, *curr; 
char comando[1024]; 
char data[1024]; 

data[0] = '\0'; 
fgets(comando, 512, stdin); 
prev = curr = strtok_r(comando, " ",&save); 

while (curr != NULL) { 
    prev = curr; 
    curr = strtok_r(NULL, " ", &save); 
    if (curr != NULL) 
     strcat(data, prev); 
} 

key = prev; 

참고 : 여기에 몇 가지 코드입니다. 명령

data[0] = '\0'; 

strcat는 첫 번째 호출에 널 종료 바이트를 발견 있는지가 확인하는 것입니다.

의 사용을 직접 key으로 바꿀 수 있습니다. 코드를 더 쉽게 읽을 수 있도록 남겨 두었습니다.

조언의 말씀 : 항상 strtok은 인자를 파괴적으로 변경한다는 점을 기억하십시오. (당신은 구분 바이트의 신원을 잃어 버립니다.) 그리고 상수 문자열로 그것을 호출 할 수 없다는 것을 기억하십시오.

참고 :data에는 모든 단어가 연결됩니다. 너는 그 공간을 잃는다. 이것이 당신이 원하는 것인지 확실하지 않습니다. 그렇지 않은 경우 strcat보다 나은 것을 사용하고 싶을 수도 있습니다 (매우 효율적이지는 않습니다. btw).예를 들어 sprintf을 사용하여 data에 토큰을 인쇄하고 공백이있는 다음 위치에 대한 포인터를 data에 유지합니다.

1
나는 다음과 같은 코드를 사용하여 루프를 대체하는 제안

(의 printf() 단지 테스트에 사용됩니다) :

또한
strtok_r(comando, " ", &save); 
char *res = NULL; 
while (NULL != (res = strtok_r(NULL, " ", &save))) { 
    if (key != NULL) { 
    //strcat(data, key); // FIXME 
    printf("data = %s\n", key); 
    } 
    key = res; 
} 
printf("key = %s\n", key); 

strcat와() NULL 인수를 사용할 수 없습니다 - 그것은 충돌에 이르게 . 따라서 데이터 포인터는 일부 배열을 가리켜 야합니다. 코드의 실행 결과 : 코드 나중에

char *key = NULL, *data=NULL, *save=NULL; 

┌─(16:08:22)─([email protected])─(~/tmp/strtok) 
└─► gcc -o main main.c; echo "one two three four five" | ./main 
data=two 
data=three 
data=four 
key = five 
1

많은 잘못, 당신은 data에 문자열을 추가 할 strcat를 사용하고 있지만 data에는 스토리지를 할당 없다. 세그먼트 화 오류가 발생합니다.

fgets(comando, 512, stdin); 

fgets는 전달 된 수보다 최대 한 적은을 읽습니다. 따라서 사용자가 512 자로 입력 할 경우 문자열의 종료 문자는 \n입니다. 또한 오류 또는 파일 끝을 감지하는 유일한 방법은 fgets의 반환 결과를 확인하는 것입니다. 그것이 NULL이면 파일 끝에 도달했거나 (사용자가 ctrl-d를 눌렀습니다) 오류가 발생했습니다. 두 경우 모두 버퍼 내용이 불확실합니다.

while(strcmp(save,"\n")) 

난 당신이 당신의 save 포인터가 사용되지 않은 문자열의 나머지 부분을 가리 킵니다 가정에 의존 할 수 있다고 생각하지 않습니다. 그것은 NULL 포인터를 리턴하여 데이터의 끝에 도달

strtok_r(comando, " ",&save); 

strtok_r 신호. 당신은 그것을 보지 않고 반환 결과를 버릴 수 없습니다. 또한 마지막 토큰의 일부로 후행 \n을 사용합니다.

strcat(data,strtok_r(NULL," ",&save)); 

나는 data가 널 포인터입니다 전에 말했듯이.

char* currentTok = strtok_r(commando, " \n", &save); // separator is space or \n 
char* previousTok = NULL; 
while (currentTok != NULL) 
{ 
    if (previousTok != NULL) 
    { 
     // save previousTok in data unless its the first token 
    } 
    previousTok = currentTok; 
    currentTok = strtok_r(NULL, " \n", &save); 
} 
char* key = previousTok;