Leikkaustoiminto sarakkeessa C

Yritän kirjoittaa idiomaattista leikkaustoimintoa C. Kuinka tämä näyttää? Pitäisikö minun sen sijaan malloksoida uusi merkkijono ja palauttaa se?

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

Kommentit

  • Siellä ’ sa useita koodin aiheuttamia ongelmia, se ’ on alttiina puskurin ylivuotohyökkäyksille ja se ei ’ t tee mitä tavallinen ” trim ” -toiminto tekee. Leikkaus poistaa etu- ja takareitit. Tämä riisuu ne kaikki.
  • Kiitos. Voitteko kertoa tarkemmin, miten puskurin ylivuotohyökkäyksiä käsitellään?
  • Älä koskaan kopioi tietoja sokeasti puskuriin, kun et tiedä ’ et tiedä kuinka paljon tilaa on varattu sille, että ’ kysyy vain ongelmia. Yksinkertainen asia olisi lisätä parametri, joka ottaa puskurin koon. Tällä tavalla ’ soittaa soittajalle kaikki, kuinka suuri se todella on. Sitten ’ riippuu siitä, ettet koskaan yritä lukea / kirjoittaa tietyn pituuden yli. Tietysti se ’ ei ole huijausta, soittaja voi antaa sinulle väärät pituudet, mutta se olisi ongelma heidän, ei sinun.

vastaus

Kuten @JeffMercado huomautti, tämä poistaa välilyöntejä sen sijaan, että leikkaisi etu- ja lopputiloja. Oletetaan, että haluat säilyttää nykyisen toiminnallisuuden, kutsumme sitä nimellä remove_spaces.

Tässä on todella hienovarainen virhe:

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

isspace ottaa allekirjoittamattoman merkin tai EOF. Sen välittäminen char, joka yleensä allekirjoitetaan, tuottaa määrittelemätöntä käyttäytymistä. Sano sen sijaan:

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

Toinen virhe: et lähetä NUL-päätelaitetta, mikä tarkoittaa, että soittajalla ei ole mitään tapaa tietää, kuinka kauan merkkijono on ( paitsi jos se nollaa puskurin ennen funktion kutsumista).

Näiden virheiden korjaaminen antaa meille:

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

Myös @JeffMercado sanoi tämän toiminnon on altis puskurin ylivuotolle. Tavallaan tämä ei ole totta, jos soittaja tietää varaavan vähintään strlen(input) + 1 -puskurin. Mutta soittaja saattaa olla laiska ja sanoa vain char result[100]. Lähtöpuskurin koon parametrin lisääminen estää todennäköisesti tällaisen virheen:

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

Katso, voitko toteuttaa tämän Joitakin huomioitavia asioita:

  • Älä unohda NUL-päätettä, kun tarkistat lähtöpuskurin kokoa.

  • Älä ole kuin strncpy ja jätä NUL-pääte pois päältä, kun merkkijono on katkaistava, koska se voi johtaa hienovaraisiin virheisiin.

  • Jos käytät int i ja j ja size_t kohteelle output_size, sinun tulisi saada kääntäjän varoitukset allekirjoitettujen ja allekirjoittamattomien vertailusta. Jos et, käännä kääntäjän varoituksia. Jos käytät GCC: tä komentoriviltä, kirjoita gcc -Wall -W.

kommentit

  • strncpy() ei ole merkkijono-funktio vaikka jotkut olettavat. Joten merkkijonon tulos olisi joka tapauksessa tapahtuma. Mikä tekee analogiasta parhaimmillaan luonnostavan.

Vastaa

Tiedämme, että voimme siirtää osoitinta eteenpäin ja taaksepäin , ja tiedämme myös, että voimme leikata merkkijonon vasemmalta. Jos kasvatamme osoitinta ja vähennämme osoitinta trimmaamaan oikealta, niin kaksi while -silmukkaa riittää. Huomaat, että oikean käyntien määrä on pienempi kuin vasemman käyntien määrä.

Oikean reunan koodi:

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

Vastaa

Helpoin tapa (poistaa vain välilyönnit):

Trim.Start:

  1. Vertaa merkkejä ne ovat " " (välilyönti tai muut merkit, kuten \n tai \t) merkkijonon alussa ja increment temp (i) -muuttuja.
  2. Siirrä osoitin i (str+=i

Trim.End:

  1. Tee sama kuin Trim.Start-toiminnolle, mutta merkkijonon lopusta.
  2. Aseta viimeiseksi merkiksi (viimeinen välilyönti) \0.

Tärkeää on, että funktio vie osoittimen osoittimeen (merkkijono).Tarkkaile funktion kutsua: 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; } 

Käyttö:

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

Tulos :

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

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *