C로 관용적 트림 기능을 작성하려고합니다. 어떻게 생겼습니까? 대신 새 문자열을 malloc하고 반환해야합니까?
void trim(const char *input, char *result) { int i, j = 0; for (i = 0; input[i] != "\0"; i++) { if (!isspace(input[i])) { result[j++] = input[i]; } } }
댓글
- 저기 ' 해당 코드에 여러 문제가 있으며 ' 버퍼 오버 플로우 공격에 취약하며 ' 일반적인 " trim " 함수가하는 일을하지 마십시오. 트림은 선행 및 후행 공백을 제거합니다. 이렇게하면 모두 제거됩니다.
- 감사합니다. 버퍼 오버플로 공격을 처리하는 방법에 대해 자세히 설명해 주시겠습니까?
- 할당 된 공간의 양을 알지 못하는 경우 ' 데이터를 맹목적으로 일부 버퍼에 복사해서는 안됩니다. '는 단지 문제를 요구하는 것입니다. 간단한 방법은 버퍼 크기를받는 매개 변수를 추가하는 것입니다. 이렇게하면 ' 모두 발신자가 실제로 얼마나 큰지 알려줍니다. 그러면 ' 주어진 길이를 초과하여 읽기 / 쓰기를 시도하지 않는 것은 사용자에게 달려 있습니다. 당연히 ' 바보 증거는 아닙니다. 발신자가 가짜 길이를 제공 할 수는 있지만 그게 문제가 될 수 있습니다.
Answer
@JeffMercado가 지적했듯이 이것은 선행 및 후행 공백을 자르는 대신 공백을 제거합니다. 현재 기능을 유지하고 싶다면 remove_spaces
라고 부르겠습니다.
여기에 정말 미묘한 버그가 있습니다.
... isspace(input[i]) ...
isspace
는 부호없는 문자 또는 EOF
. 일반적으로 서명되는 char
를 전달하면 정의되지 않은 동작이 생성됩니다. 대신 다음과 같이 말하십시오.
... isspace((unsigned char) input[i]) ...
또 다른 버그 : NUL 터미네이터를 방출하지 않습니다. 이는 호출자가 문자열이 얼마나 긴지 알 수있는 방법이 없음을 의미합니다. 함수를 호출하기 전에 버퍼를 제로화하지 않는 한).
이러한 버그를 수정하면 다음을 얻을 수 있습니다.
void remove_spaces(const char *input, char *result) { int i, j = 0; for (i = 0; input[i] != "\0"; i++) { if (!isspace((unsigned char) input[i])) { result[j++] = input[i]; } } result[j] = "\0"; }
@JeffMercado도이 함수를 언급했습니다. 버퍼 오버 플로우에 취약합니다. 어떤 의미에서 호출자가 최소한 strlen(input) + 1
의 버퍼를 할당하는 것을 알고 있다면 이것은 사실이 아닙니다.하지만 호출자는 게으르고 그냥 char result[100]
. 출력 버퍼 크기 매개 변수를 추가하면 이러한 실수를 방지 할 수 있습니다.
void remove_spaces(const char *input, char *output, size_t output_size);
이것을 구현할 수 있는지 확인하십시오. . 몇 가지 유의 사항 :
-
출력 버퍼 크기를 확인할 때 NUL 종료자를 잊지 마십시오.
-
strncpy 처럼되지 말고 문자열을 잘라야 할 때 NUL 종결자를 생략하면 미묘한 버그가 발생할 수 있습니다.
-
i
및j
에int
를 사용하는 경우size_t
output_size
의 경우 서명 된 것과 서명되지 않은 것 간의 비교에 대한 컴파일러 경고를 받아야합니다. 그렇지 않으면 컴파일러 경고를 표시합니다. 명령 줄에서 GCC를 사용하고 있다면gcc -Wall -W
를 입력하는 습관을들이세요.
댓글
-
strncpy()
아닙니다 . 일부는 추측하지만. 따라서 결과는 어쨌든 우연이 될 것입니다. 비유는 기껏해야 대략적입니다.
답변
포인터를 앞뒤로 움직일 수 있다는 것을 알고 있습니다. , 우리는 또한 왼쪽에서 문자열을자를 수 있다는 것을 알고 있습니다. 포인터를 증가시키고 포인터를 감소시켜 오른쪽에서 트리밍하면 두 개의 while
루프로 충분합니다. 오른쪽 보행 횟수가 왼쪽 보행 횟수보다 적다는 것을 알 수 있습니다.
오른쪽 트림 코드 :
#include <stdio.h> #include <ctype.h> void trim_both(char *, char *); int main (void) { char title[100] = " My long string "; char title_t[100] = ""; (void) printf("String before left trim is:[%s]\n", title); trim_both(title, title_t); (void) printf("String after left trim is:[%s]\n", title_t); } // trim spaces from left void trim_both(char *title_p, char *title_tp) { int flag = 0; // from left while(*title_p) { if(!isspace((unsigned char) *title_p) && flag == 0) { *title_tp++ = *title_p; flag = 1; } title_p++; if(flag == 1) { *title_tp++ = *title_p; } } // from right while(1) { title_tp--; if(!isspace((unsigned char) *title_tp) && flag == 0) { break; } flag = 0; *title_tp = "\0"; } }
Answer
가장 쉬운 방법 (공백 만 제거) :
Trim.Start :
- 다음까지 문자 비교 문자열 시작에서
" "
(공백 또는\n
또는\t
와 같은 기타 문자)와 같습니다. 온도 (i
) 변수를 증가시킵니다. - 포인터를
i
(str+=i
Trim.End :
- Trim.Start의 경우와 동일하지만 문자열의 끝에서 수행합니다.
- 마지막 문자 (마지막 공백)를
\0
로 설정합니다.
중요한 것은 함수가 포인터 (문자열)에 대한 포인터를 취한다는 것입니다.함수 호출을 확인합니다. StringTrim(&p2);
char * StringTrim(char * *pointerToString) { u8 start=0, length=0; // Trim.Start: length = strlen(*pointerToString); while ((*pointerToString)[start]==" ") start++; (*pointerToString) += start; if (start < length) // Required for empty (ex. " ") input { // Trim.End: u8 end = strlen(*pointerToString)-1; // Get string length again (after Trim.Start) while ((*pointerToString)[end]==" ") end--; (*pointerToString)[end+1] = 0; } return *pointerToString; }
사용법 :
char str1[] = " test1 "; char * p1 = str1; Debug("1. before trim: [%s]", p1); StringTrim(&p1); Debug("1. after trim [%s]", p1); char str2[] = " test2"; char * p2 = str2; Debug("2. before trim: [%s]", p2); StringTrim(&p2); Debug("2. after trim [%s]", p2); char str3[] = "test3 "; char * p3 = str3; Debug("3. before trim: [%s]", p3); StringTrim(&p3); Debug("3. after trim [%s]", p3); char str4[] = " "; char * p4 = str4; Debug("4. before trim: [%s]", p4); StringTrim(&p4); Debug("4. after trim [%s]", p4); char str5[] = ""; char * p5 = str5; Debug("5. before trim: [%s]", p5); StringTrim(&p5); Debug("5. after trim [%s]", p5);
결과 :
1. before trim: [ test1 ] 1. after trim [test1] 2. before trim: [ test2] 2. after trim [test2] 3. before trim: [test3 ] 3. after trim [test3] 4. before trim: [ ] 4. after trim [] 5. before trim: [] 5. after trim []