Funkcja przycinania w C

Próbuję napisać idiomatyczną funkcję przycinania w C. Jak to wygląda? Czy powinienem zamiast tego mallocować nowy ciąg i zwrócić go?

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

Komentarze

  • Tam ' z wieloma problemami z tym kodem, jest on ' podatny na ataki przepełnienia bufora i nie ' rób to, co robi typowa funkcja ” przycinania „. Przycinanie usuwa wiodące i końcowe spacje. To usuwa je wszystkie.
  • Dzięki. Czy możesz wyjaśnić, jak radzić sobie z atakami przepełnienia bufora?
  • Nigdy nie powinieneś ślepo kopiować danych do jakiegoś bufora, jeśli nie ' nie wiesz, ile miejsca jest przydzielone do niego, że ' prosi tylko o kłopoty. Prostą rzeczą byłoby dodanie parametru, który pobiera rozmiar bufora. W ten sposób to ' leży po stronie dzwoniącego, aby powiedzieć Ci, jak duże jest to naprawdę. Wtedy ' zależy od Ciebie, abyś nigdy nie próbował czytać / pisać więcej niż podana długość. Oczywiście ' nie jest głupim dowodem, dzwoniący może podać fałszywe długości, ale byłby to problem z ich strony, a nie z twoim.

Odpowiedź

Jak zauważył @JeffMercado, usuwa to spacje zamiast skracać spacje wiodące i końcowe. Zakładając, że chcesz zachować obecną funkcjonalność, nazwijmy to remove_spaces.

Występuje tutaj bardzo subtelny błąd:

isspace przyjmuje wartość unsigned char lub EOF. Przekazanie mu char, który zwykle jest podpisany, spowoduje niezdefiniowane zachowanie. Zamiast tego powiedz:

... isspace((unsigned char) input[i]) ... 

Kolejny błąd: nie emitujesz terminatora NUL, co oznacza, że dzwoniący nie miałby możliwości dowiedzieć się, jak długi jest ciąg ( chyba że wyzerował bufor przed wywołaniem funkcji).

Po naprawieniu tych błędów otrzymujemy:

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 również powiedział tę funkcję jest podatny na przepełnienie bufora. W pewnym sensie to nieprawda, pod warunkiem, że dzwoniący wie, że powinien przydzielić bufor o wartości co najmniej strlen(input) + 1. Jednak dzwoniący może być leniwy i po prostu powiedzieć char result[100]. Dodanie parametru rozmiaru bufora wyjściowego prawdopodobnie uchroni przed takim błędem:

void remove_spaces(const char *input, char *output, size_t output_size); 

Sprawdź, czy możesz to zaimplementować . Kilka rzeczy, o których należy pamiętać:

  • Nie zapomnij o zakończeniu NUL podczas sprawdzania rozmiaru bufora wyjściowego.

  • Nie zachowuj się jak strncpy i pomijaj terminator NUL, gdy musisz skrócić ciąg, ponieważ może to prowadzić do drobnych błędów.

  • Jeśli używasz int dla i i j i size_t w przypadku output_size powinny pojawić się ostrzeżenia kompilatora dotyczące porównania między znakiem i bez znaku. Jeśli tego nie zrobisz, podkręć ostrzeżenia kompilatora. Jeśli używasz GCC z wiersza poleceń, przyzwyczaj się do wpisywania gcc -Wall -W.

Komentarze

  • strncpy() nie jest funkcją ciągową, nawet choć niektórzy zakładają. Tak więc wynik będący ciągiem i tak byłby zdarzeniem. Co sprawia, że analogia jest w najlepszym przypadku szkicowa.

Odpowiedź

Wiemy, że możemy przesuwać wskaźnik do przodu i do tyłu , i wiemy również, że możemy przyciąć ciąg z lewej strony. Jeśli zwiększymy wskaźnik i zmniejszymy wskaźnik, aby przyciąć go od prawej strony, wystarczą dwie while pętle. Zauważysz, że liczba kroków po prawej stronie jest mniejsza niż liczba kroków po lewej stronie.

Kod prawego przycięcia:

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

Odpowiedź

Najłatwiejszy sposób (usuwa tylko spacje):

Przytnij.Start:

  1. Porównaj znaki do są równe " " (spacja lub inne znaki, takie jak \n lub \t) na początku ciągu i wzrost zmiennej temp (i).
  2. Przesuń wskaźnik o i (str+=i). Teraz ciąg zaczyna się od znaku, który nie jest znakiem spacji (ani żadnym innym białym znakiem).

Trim.End:

  1. Zrób to samo dla Trim.Start, ale od końca ciągu.
  2. Ustaw ostatni znak (ostatnią spację) jako \0.

Ważną rzeczą jest to, że funkcja pobiera wskaźnik do wskaźnika (string).Uważaj na wywołanie funkcji: 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; } 

Użycie:

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

Wynik :

 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 []  

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *