Trimfunksjon i C

Jeg prøver å skrive en idiomatisk trimfunksjon i C. Hvordan ser dette ut? Skal jeg i stedet malloc den nye strengen og returnere den?

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

Kommentarer

  • Der ‘ et antall problemer med den koden, det ‘ er sårbart for bufferoverløpsangrep, og det ‘ t gjør hva en typisk » trim » -funksjon. Trim fjerner ledende og etterfølgende mellomrom. Dette fjerner dem alle.
  • Takk. Kan du snakke om hvordan du håndterer bufferoverløpsangrep?
  • Du bør aldri blindt kopiere data til noen buffer når du ikke ‘ ikke vet hvor mye plass som er tildelt til det, at ‘ bare ber om problemer. En enkel ting å gjøre vil være å legge til en parameter som tar inn størrelsen på bufferen. På den måten er det ‘ som ringer opp for å fortelle deg hvor stor den egentlig er. Så er det ‘ opp til deg å aldri prøve å lese / skrive utover gitt lengde. Selvfølgelig er det ‘ ikke idiotsikkert, den som ringer kan gi deg falske lengder, men det ville være et problem i deres ende, ikke din.

Svar

Som @JeffMercado påpekte, fjerner dette mellomrom i stedet for å trimme ledende og etterfølgende mellomrom. Forutsatt at du vil beholde den nåværende funksjonaliteten, la oss kalle det remove_spaces.

Det er en veldig subtil feil her:

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

isspace tar verdien av en usignert tegn eller EOF. Hvis du sender det til char, som vanligvis er signert, vil det føre til udefinert oppførsel. Si i stedet:

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

En annen feil: du sender ikke ut en NUL-terminator, noe som betyr at den som ringer ikke har noen måte å vite hvor lang strengen er ( med mindre den nulstilte bufferen før du ringte til funksjonen din.

Å fikse disse feilene gir oss:

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 sa også denne funksjonen er sårbar for bufferoverløp. På en måte er dette ikke sant, forutsatt at den som ringer vet å tildele en buffer på minst strlen(input) + 1. Men innringeren kan være lat og bare si char result[100]. Å legge til en parameter for utgangsbufferstørrelse vil sannsynligvis beskytte mot en slik feil:

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

Se om du kan implementere dette Noen ting å huske på:

  • Ikke glem NUL-terminatoren når du sjekker størrelsen på utgangsbufferen.

  • Ikke vær som strncpy og utelat NUL-terminatoren når du må avkutte strengen, da det kan føre til subtile feil.

  • Hvis du bruker int for i og j og size_t for output_size, bør du få kompilatoradvarsler om sammenligning mellom signert og usignert. Hvis du ikke gjør det, skru opp kompileringsadvarslene dine. Hvis du bruker GCC fra kommandolinjen, må du vane å skrive gcc -Wall -W.

Kommentarer

  • strncpy() er ikke en strengfunksjon, selv selv om noen antar det. Så resultatet å være en streng vil uansett være tilfelle. Som i beste fall gjør analogien sketchy.

Svar

Vi vet at vi kan bevege en peker fremover og bakover , og vi vet også at vi kan trimme en streng fra venstre. Hvis vi øker pekeren og reduserer pekeren for å trimme fra høyre, er det nok med

sløyfer. Du vil merke at antall gange for høyre er mindre enn antall gange for venstre.

Høyre trim-kode:

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

Svar

Enkleste måte (fjerner bare mellomrom):

Trim. Start:

  1. Sammenlign tegn til de tilsvarer " " (mellomrom eller andre tegn som \n eller \t) ved strengstart og øk temp (i) variabel.
  2. Flytt pekeren rundt i (str+=i). Nå starter strengen fra røye som ikke er mellomrom (eller annen hvit røye).

Trim.End:

  1. Gjør det samme for Trim.Start men fra slutten av strengen.
  2. Sett siste tegn (siste mellomrom) som \0.

Det viktige er at funksjonen tar pekeren til pekeren (streng).Se etter funksjonsanropet: 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; } 

Bruk:

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

Resultat :

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

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *