Funkce oříznutí v C

Pokouším se napsat idiomatickou funkci oříznutí do C. Jak to vypadá? Měl bych místo toho znehodnotit nový řetězec a vrátit ho?

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

Komentáře

  • Tam ‚ je řada problémů s tímto kódem, je ‚ zranitelný útoky přetečení vyrovnávací paměti a ‚ Nedělá to, co dělá typická “ trim “ funkce. Oříznutí odstraní přední a zadní mezery. To je všechny odstraní.
  • Díky. Můžete vysvětlit, jak zacházet s útoky na přetečení vyrovnávací paměti?
  • Nikdy byste neměli slepě kopírovat data do nějaké vyrovnávací paměti, když nevíte, kolik místa je přiděleno ‚ k tomu, že ‚ jen žádá o potíže. Jednoduchou věcí by bylo přidat parametr, který zabírá velikost vyrovnávací paměti. Tímto způsobem ‚ je na volajícím, aby vám řekl, jak velký je ve skutečnosti. Pak je ‚ na vás, abyste se nikdy nepokusili číst / zapisovat nad danou délku. Samozřejmě to ‚ není bláznivý důkaz, volající vám může poskytnout falešné délky, ale to by byl problém na jejich konci, ne váš.

Odpovědět

Jak zdůraznil @JeffMercado, místo ořezávání mezer před a za mezerou budou odstraněny mezery. Za předpokladu, že si chcete zachovat aktuální funkčnost, pojďme to nazvat remove_spaces.

Tady je opravdu drobná chyba:

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

isspace nabývá hodnoty nepodepsaného char nebo EOF. Pokud jej předáte char, který je obvykle podepsán, způsobí to nedefinované chování. Místo toho řekněte:

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

Další chyba: nevydáte terminátor NUL, což znamená, že volající nebude mít žádný způsob, jak zjistit, jak dlouhý je řetězec ( pokud nevynuluje vyrovnávací paměť před voláním vaší funkce).

Oprava těchto chyb nám dává:

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 také řekl tuto funkci je zranitelný přetečením vyrovnávací paměti. V jistém smyslu to není pravda, pokud volající ví, že přiděluje vyrovnávací paměť alespoň strlen(input) + 1. Volající však může být líný a stačí říct char result[100]. Přidání parametru velikosti výstupní vyrovnávací paměti bude pravděpodobně chráněno před takovou chybou:

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

Zjistěte, zda toto můžete implementovat Je třeba mít na paměti několik věcí:

  • Nezapomeňte při kontrole velikosti výstupní vyrovnávací paměti na zakončení NUL.

  • Nebuďte jako strncpy a nevynechejte zakončení NUL, pokud budete muset řetězec zkrátit, protože by to mohlo vést k drobným chybám.

  • Pokud používáte int pro i a j a size_t pro output_size byste měli dostat upozornění kompilátoru na srovnání mezi podepsaným a nepodepsaným. Pokud tak neučiníte, zobrazte varování kompilátoru. Pokud používáte GCC z příkazového řádku, zvykněte si psát gcc -Wall -W.

Komentáře

  • strncpy() není funkce řetězce, i když ačkoli někteří předpokládají. Takže výsledek, který je řetězcem, by se stejně stal. Díky tomu je analogie v nejlepším případě povrchní.

Odpověď

Víme, že můžeme pohybovat ukazatelem dopředu a dozadu a také víme, že můžeme oříznout řetězec zleva. Pokud zvýšíme ukazatel a zmenšíme ukazatel na oříznutí zprava, stačí dvě smyčky while. Všimnete si, že počet kroků chůze je menší než počet chůze vlevo.

Kód pro oříznutí vpravo:

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

Odpovědět

Nejjednodušší způsob (odstraní pouze mezery):

Trim.Start:

  1. Porovnat znaky do rovná se " " (mezera nebo jiné znaky jako \n nebo \t) na začátku řetězce a přírůstek temp (i) proměnná.
  2. Přesuňte ukazatel na i (str+=i). Řetězec nyní začíná znakem, který není znakem mezery (ani žádným jiným bílým znakem).

Oříznout konec:

  1. Udělejte to samé pro Trim.Start, ale od konce řetězce.
  2. Nastavit poslední znak (poslední mezeru) jako \0.

Důležité je, že funkce bere ukazatel na ukazatel (řetězec).Sledujte volání funkce: 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; } 

Použití:

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

Výsledek :

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

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *