Încerc să scriu o funcție de tăiere idiomatică în C. Cum arată? Ar trebui să înlocuiesc noul șir și să îl returnez?
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]; } } }
Comentarii
- Acolo ‘ un număr de probleme cu codul respectiv, ‘ este vulnerabil la atacurile de depășire a bufferului și nu ‘ nu faceți ceea ce face o funcție tipică ” trim „. Trim elimină spațiul alb principal și final. Acest lucru le elimină pe toate.
- Mulțumesc. Puteți să vă explicați cum să gestionați atacurile de depășire a bufferului?
- Nu ar trebui să copiați orbește date în vreun buffer atunci când nu ‘ știți cât spațiu este alocat la asta, că ‘ cere doar probleme. Un lucru simplu de făcut ar fi să adăugați un parametru care să ia în considerare dimensiunea bufferului. În acest fel, ‘ este totul pe apelant pentru a vă spune cât de mare este cu adevărat. Apoi, ‘ depinde de dvs. să nu încercați niciodată să citiți / scrieți dincolo de lungimea dată. Desigur, ‘ nu este o dovadă de prostie, apelantul îți poate oferi lungimi false, dar asta ar fi o problemă pe partea lor, nu a ta.
Răspuns
Așa cum a subliniat @JeffMercado, acest lucru elimină spațiile în loc de tăierea spațiilor de conducere și de urmărire. Presupunând că doriți să păstrați funcționalitatea curentă, să o numim remove_spaces
.
Există o eroare foarte subtilă aici:
... isspace(input[i]) ...
isspace
ia valoarea unui nesemnat sau EOF
. Dacă îl treceți pe un char
, care este semnat de obicei, va produce un comportament nedefinit. În schimb, spuneți:
... isspace((unsigned char) input[i]) ...
O altă eroare: nu emiteți un terminator NUL, ceea ce înseamnă că apelantul nu ar avea cum să știe cât de lung este șirul ( cu excepția cazului în care a redus la zero bufferul înainte de a apela funcția dvs.).
Remedierea acestor erori ne oferă:
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 a mai spus această funcție este vulnerabil la depășirea bufferului. Într-un sens, acest lucru nu este adevărat, cu condiția ca apelantul să știe să aloce un buffer de cel puțin strlen(input) + 1
. Dar apelantul ar putea fi leneș și să spună doar char result[100]
. Adăugarea unui parametru de dimensiune a bufferului de ieșire va proteja probabil împotriva unei astfel de greșeli:
void remove_spaces(const char *input, char *output, size_t output_size);
Vedeți dacă puteți implementa acest lucru . Unele lucruri de reținut:
-
Nu uitați de terminatorul NUL atunci când verificați dimensiunea bufferului de ieșire.
-
Nu fi ca strncpy și omiteți terminatorul NUL atunci când trebuie să tăiați șirul, deoarece poate duce la erori subtile.
-
Dacă utilizați
int
pentrui
șij
șisize_t
pentruoutput_size
, ar trebui să primiți avertismente ale compilatorului despre comparația dintre semnat și nesemnat. Dacă nu faceți acest lucru, activați avertismentele compilatorului. Dacă utilizați GCC din linia de comandă, obișnuiți să tastațigcc -Wall -W
.
Comentarii
-
strncpy()
nu este o funcție șir, chiar deși unii presupun. Deci, rezultatul fiind un șir ar fi oricum întâmplător. Ceea ce face ca analogia să fie incompletă în cel mai bun caz.
Răspuns
Știm că putem muta un indicator înainte și înapoi și știm, de asemenea, că putem tăia un șir din stânga. Dacă incrementăm indicatorul și decrementăm indicatorul pentru a-l tăia din dreapta, atunci sunt suficiente două bucle while
. Veți observa că numărul de plimbări din dreapta este mai mic decât numărul de plimbări din stânga.
Cod de tăiere la dreapta:
#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"; } }
Răspuns
Cel mai simplu mod (elimină doar spațiile):
Trim.Start:
- Comparați litere până acestea sunt egale cu
" "
(spațiu sau alte caractere precum\n
sau\t
) la începutul șirului și variabila de creștere temp (i
). - Mutați indicatorul pe
i
(str+=i
). Acum șirul începe de la un caracter care nu este un spațiu (sau orice alt caracter alb).
Trim.End:
- Faceți același lucru cu Trim.Start, dar de la sfârșitul șirului.
- Setați ultimul caracter (ultimul spațiu) ca
\0
.
Important este că funcția duce pointer to pointer (șir).Urmăriți apelul funcției: 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; }
Utilizare:
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);
Rezultat :
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 []