C-standardifunktiot merkkijonojen kanssa työskentelemiseen. Toiminnot merkkijonojen käsittelyyn C:ssä. Vakiokirjaston päätoiminnot string.h

Keskeytä AdBlock tällä sivustolla.

Eli merkkijonot C-kielellä. Niille ei ole erillistä tietotyyppiä, kuten monissa muissa ohjelmointikielissä tehdään. C:ssä merkkijono on merkkijono. Merkitsemään rivin loppua käytetään "\0"-merkkiä, josta keskustelimme tämän oppitunnin viimeisessä osassa. Sitä ei näytetä näytöllä millään tavalla, joten et voi katsoa sitä.

Merkkijonon luominen ja alustaminen

Koska merkkijono on merkkijono, merkkijonon ilmoittaminen ja alustaminen ovat samanlaisia ​​kuin samanlaiset toiminnot yksiulotteisten taulukoiden kanssa.

Seuraava koodi havainnollistaa eri tapoja alustaa merkkijonoja.

Listaus 1.

Char str; char str1 = ("Y", "o", "n", "g", "C", "o", "d", "e", "r", "\0"); char str2 = "Hei!"; char str3 = "Hei!";

Kuva 1 Merkkijonojen ilmoittaminen ja alustus

Ensimmäisellä rivillä kerromme yksinkertaisesti kymmenen merkin taulukon. Se ei ole edes varsinainen merkkijono, koska... siinä ei ole tyhjämerkkiä \0, toistaiseksi se on vain joukko merkkejä.

Toinen linja. Yksinkertaisin tapa alustus otsassa. Ilmoitamme jokaisen symbolin erikseen. Tärkeintä tässä ei ole unohtaa lisätä tyhjää merkkiä \0 .

Kolmas rivi on analoginen toisen rivin kanssa. Kiinnitä huomiota kuvaan. Koska Oikeanpuoleisella rivillä on vähemmän merkkejä kuin taulukossa on elementtejä, loput elementit täytetään merkillä \0 .

Neljäs rivi. Kuten näet, kokoa ei ole määritelty tässä. Ohjelma laskee sen automaattisesti ja luo tarvittavan pituisen merkkijonon. Tässä tapauksessa tyhjä merkki \0 lisätään viimeisenä.

Kuinka tulostaa merkkijono

Laajennetaan yllä oleva koodi täysimittaiseksi ohjelmaksi, joka näyttää luodut rivit näytöllä.

Listaus 2.

#sisältää int main(void) ( char str; char str1 = ("Y","o", "n", "g", "C", "o","d", "e", "r"," \0"); char str2 = "Hei!"; char str3 = "Hei!"; for(int i = 0; i< 10; i = i + 1) printf("%c\t",str[i]); printf("\n"); puts(str1); printf("%s\n",str2); puts(str3); return 0; }


Kuva 2 Eri tapoja näyttää merkkijono näytöllä

Kuten näet, on useita perustapoja näyttää merkkijono näytöllä.

  • käytä printf-funktiota %s-määritteen kanssa
  • käytä puts-toimintoa
  • käytä fputs-funktiota ja määritä lähdön vakiovirta toiseksi parametriksi stdout.

Ainoa vivahde liittyy puts- ja fputs-toimintoihin. Huomaa, että puts-funktio rivittää tulosteen seuraavalle riville, mutta fputs-funktio ei.

Kuten näette, johtopäätös on melko yksinkertainen.

Merkkijonojen syöttäminen

Merkkijonon syöttö on hieman monimutkaisempi kuin tulostus. Yksinkertaisin tapa olisi seuraava:

Listaus 3.

#sisältää int main(void) ( char str; gets(str); puts(str); return 0; )

Gets-funktio keskeyttää ohjelman, lukee näppäimistöltä syötetyn merkkijonon ja sijoittaa sen merkkijonoon, jonka nimi välitetään funktiolle parametrina.
Gets-funktio poistuu enter-näppäintä vastaavalla merkillä ja kirjoitetaan merkkijonoon tyhjäksi.
Huomasitko vaaran? Jos ei, kääntäjä ystävällisesti varoittaa siitä. Ongelmana on, että gets-toiminto poistuu vasta, kun käyttäjä painaa enteriä. Tämä on täynnä sitä tosiasiaa, että voimme ylittää taulukon meidän tapauksessamme - jos syötetään yli 20 merkkiä.
Muuten, puskurin ylivuotovirheitä pidettiin aiemmin yleisimpänä haavoittuvuuden tyyppinä. Ne ovat edelleen olemassa, mutta niiden käyttäminen ohjelmien hakkerointiin on tullut paljon vaikeammaksi.

Mitä meillä on? Meillä on tehtävä: kirjoita merkkijono rajoitetun kokoiseen taulukkoon. Eli meidän on jotenkin kontrolloitava käyttäjän syöttämien merkkien määrää. Ja tässä fgets-toiminto tulee avuksemme:

Listaus 4.

#sisältää int main(void) ( char str; fgets(str, 10, stdin); puts(str); return 0; )

Fgets-funktio ottaa syötteenä kolme argumenttia: muuttujan, johon merkkijono kirjoitetaan, kirjoitettavan merkkijonon koon ja virran nimen, josta tiedot saadaan kirjoitettaviksi merkkijonoon, tässä tapauksessa stdin. Kuten jo tiedät oppitunnista 3, stdin on tavallinen syöttövirta, joka yleensä liitetään näppäimistöön. Tietojen ei välttämättä tarvitse tulla stdin-virrasta, vaan jatkossa käytämme tätä toimintoa myös datan lukemiseen tiedostoista.

Jos tätä ohjelmaa suoritettaessa syötetään yli 10 merkkiä pitempi merkkijono, taulukkoon kirjoitetaan silti vain 9 merkkiä alusta ja rivinvaihto, fgets "leikkaa" merkkijonon haluttuun pituuteen.

Huomaa, että fgets-funktio ei lue 10 merkkiä, vaan 9! Kuten muistamme, merkkijonoissa viimeinen merkki on varattu nollamerkille.

Katsotaanpa se. Suoritetaan ohjelma viimeisestä listasta. Ja syötä rivi 1234567890. Rivi 123456789 tulee näkyviin näytölle.


Kuva 3 Esimerkki fgets-funktiosta

Herää kysymys. Minne kymmenes hahmo katosi? Ja minä vastaan. Se ei ole kadonnut mihinkään, se pysyy syöttövirrassa. Suorita seuraava ohjelma.

Listaus 5.

#sisältää int main(void) ( char str; fgets(str, 10, stdin); puts(str); int h = 99; printf("do %d\n", h); scanf("%d",&h) ; printf("%d\n jälkeen", h); return 0; )

Tässä hänen työnsä tulos.


Kuva 4 Ei-tyhjä stdin-puskuri

Selitän mitä tapahtui. Kutsuimme fgets-funktiota. Hän avasi syöttövirran ja odotti, että syötämme tiedot. Syötimme 1234567890\n näppäimistöltä (\n tarkoitan Enter-näppäimen painamista). Tämä meni stdin-syöttövirtaan. Fgets-funktio otti odotetusti ensimmäiset 9 merkkiä 123456789 syöttövirrasta, lisäsi niihin nollamerkin \0 ja kirjoitti sen merkkijonoon str . Syöttövirrassa on vielä 0\n jäljellä.

Seuraavaksi määritetään muuttuja h. Näytämme sen arvon näytöllä. Sitten kutsumme scanf-funktiota. Tässä odotetaan, että voimme syöttää jotain, mutta... tulovirrassa roikkuu 0\n, sitten scanf-funktio havaitsee tämän syötteenämme ja kirjoittaa 0:n muuttujaan h. Seuraavaksi näytämme sen näytöllä.

Tämä ei tietenkään ole aivan sitä, mitä odotamme. Tämän ongelman ratkaisemiseksi meidän on tyhjennettävä syöttöpuskuri, kun olemme lukeneet käyttäjän syötteen siitä. Tätä tarkoitusta varten sitä käytetään erikoistoiminto huuhtele. Sillä on vain yksi parametri - virta, joka on tyhjennettävä.

Korjataan viimeinen esimerkki niin, että se toimii ennustettavasti.

Listaus 6.

#sisältää int main(void) ( char str; fgets(str, 10, stdin); fflush(stdin); // tyhjennä syöttövirta puts(str); int h = 99; printf("do %d\n", h ) ; scanf("%d",&h); printf("%d\n jälkeen", h); return 0; )

Nyt ohjelma toimii kuten pitääkin.


Kuva 4 stdin-puskurin huuhtelu huuhtelutoiminnolla

Yhteenvetona voidaan todeta kaksi tosiasiaa. Ensimmäinen. Päällä Tämä hetki gets-funktion käyttö ei ole turvallista, joten on suositeltavaa käyttää fgets-toimintoa kaikkialla.

Toinen. Älä unohda tyhjentää syöttöpuskuria, jos käytät fgets-toimintoa.

Tämä päättää keskustelun merkkijonojen syöttämisestä. Mene eteenpäin.

Habra, hei!

Minulle sattui vähän aikaa sitten varsin mielenkiintoinen tapaus, johon oli osallisena yksi tietotekniikan korkeakoulun opettajista.

Keskustelu Linux-ohjelmoinnista eteni hitaasti siihen, että tämä henkilö väitti monimutkaisuudesta järjestelmän ohjelmointi itse asiassa suuresti liioiteltua. Että C-kieli on yhtä yksinkertainen kuin ottelu, itse asiassa kuin Linux-ydin (hänen sanoin).

Minulla oli mukana kannettava Linuxilla varustettu tietokone, joka sisälsi herrasmiesjoukon C-kielellä kehitettävää apuohjelmaa (gcc, vim, make, valgrind, gdb). En muista, minkä tavoitteen asetimme itsellemme silloin, mutta muutaman minuutin kuluttua vastustaja huomasi olevansa tämän kannettavan tietokoneen ääressä täysin valmis ratkaisemaan ongelman.

Ja kirjaimellisesti aivan ensimmäisillä riveillä hän teki vakavan virheen varaaessaan muistia... riville.

Char *str = (char *)malloc(sizeof(char) * strlen(puskuri));
puskuri - pinomuuttuja, johon näppäimistön tiedot kirjoitettiin.

Uskon, että varmasti tulee ihmisiä, jotka kysyvät: "Kuinka tässä voi olla mitään vikaa?"
Usko minua, se voi.

Ja mitä tarkalleen - lue kissasta.

Pieni teoria - eräänlainen LikBez.

Jos tiedät, vieritä seuraavaan otsikkoon.

C:n merkkijono on merkkijono, jonka tulee aina päättyä "\0" -merkkiin, joka on rivin lopun merkki. Pinon merkkijonot (staattiset) ilmoitetaan seuraavasti:

Char str[n] = (0);
n on merkkijonon koko, sama kuin merkkijonon pituus.

Tehtävä ( 0 ) - merkkijonon "nollaus" (valinnainen, voit ilmoittaa sen ilman sitä). Tulos on sama kuin ajettaessa funktioita memset(str, 0, sizeof(str)) ja bzero(str, sizeof(str)). Sitä käytetään estämään roskien jättäminen alustamattomiin muuttujiin.

Voit myös alustaa pinossa olevan merkkijonon välittömästi:

Char buf = "oletuspuskurin teksti\n";
Lisäksi merkkijono voidaan ilmoittaa osoittimeksi ja sille voidaan varata muistia kasaan:

Char *str = malloc(koko);
koko - merkkijonolle varaamamme tavujen määrä. Tällaisia ​​merkkijonoja kutsutaan dynaamiksi (johtuen siitä, että vaadittu koko lasketaan dynaamisesti + varattua muistin kokoa voidaan milloin tahansa kasvattaa realloc()-funktiolla).

Pinomuuttujan tapauksessa taulukon koon määrittämiseen käytin merkintää n, pinomuuttujan tapauksessa merkintäkokoa. Ja tämä heijastaa täydellisesti pinossa olevan ilmoituksen ja kasaan muistivarausta sisältävän ilmoituksen välisen eron todellista olemusta, koska n:ää käytetään yleensä puhuttaessa elementtien lukumäärästä. Ja koko on aivan eri tarina...

Valgrind auttaa meitä

Mainitsin sen myös edellisessä artikkelissani. Valgrind ( , kaksi - pieni ohje) on erittäin hyödyllinen ohjelma, joka auttaa ohjelmoijaa jäljittämään muistivuotoja ja kontekstivirheitä - juuri niitä asioita, jotka ilmestyvät useimmiten merkkijonojen kanssa työskennellessä.

Katsotaanpa lyhyttä luetteloa, joka toteuttaa jotain samanlaista kuin mainitsemani ohjelma, ja suorita se valgrindin kautta:

#sisältää #sisältää #sisältää #define HELLO_STRING "Hei, Habr!\n" void main() ( char *str = malloc(sizeof(char) * strlen(HELLO_STRING)); strcpy(str, HELLO_STRING); printf("->\t%s" , str); vapaa(str); )
Ja itse asiassa ohjelman tulos:

$ gcc main.c $ ./a.out -> Hei, Habr!
Ei vielä mitään epätavallista. Suoritetaan nyt tämä ohjelma valgrindin kanssa!

$ valgrind --tool=memcheck ./a.out ==3892== Memcheck, muistivirheiden ilmaisin ==3892== Tekijänoikeus (C) 2002-2015 ja GNU GPL"d, Julian Seward et al. == 3892== Käytetään Valgrind-3.12.0:aa ja LibVEX:ää; suorita tekijänoikeustiedot uudelleen komennolla -h ==3892== Komento: ./a.out ==3892== ==3892== Virheellinen kirjoitus koko 2 ==3892= = 0x4005B4:ssä: main (in /home/indever/prg/C/public/a.out) ==3892== Osoite 0x520004c on 12 tavua 13-koon lohkossa alloc"d ==3892== osoitteessa 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== 0x400597: main (kansiossa /home/indever/prg/C/public/a.out) ==3892== ==3892== Virheellinen lukukoko 1 == 3892== 0x4C30BC4:ssä: strlen (vg_replace_strmem.c:454) ==3892== 0x4E89AD0:ssa: vfprintf (hakemistossa /usr/lib64/libc-2.24.so) ==3892== 0x4E8:ssa print/0 lib64/libc-2.24.so) ==3892== 0x4005CF:llä: main (kansiossa /home/indever/prg/C/public/a.out) ==3892== Osoite 0x520004d on 0 tavua 13-koon lohkon jälkeen alloc"d ==3892== osoitteessa 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== 0x400597:llä: main (hakemistossa /home/indever/prg/C/public/a.out) ==3892== -> Hei, Habr! ==3892== ==3892== KEON YHTEENVETO: ==3892== käytössä poistuttaessa: 0 tavua 0 lohkossa ==3892== keon kokonaiskäyttö: 2 varattua, 2 vapaata, 1 037 tavua varattu ==3892= = ==3892== Kaikki kasalohkot vapautettiin -- vuodot eivät ole mahdollisia ==3892== ==3892== Havaittujen ja estettyjen virheiden määrä, suorita uudelleen komennolla: -v ==3892== VIRHEYHTEENVETO: 3 virhettä 2 kontekstista (suljettu: 0 0:sta)
==3892== Kaikki kasalohkot vapautettiin - vuodot eivät ole mahdollisia- Ei vuotoja, mikä on hyvä uutinen. Mutta kannattaa laskea silmäsi hieman alemmas (vaikka haluan huomata, tämä on vain yhteenveto, päätiedot ovat hieman eri paikassa):

==3892== VIRHEYHTEENVETO: 3 virhettä 2 kontekstista (suljettu: 0 0:sta)
3 virhettä. 2 kontekstissa. Näin yksinkertaisessa ohjelmassa. Miten!?

Kyllä, hyvin yksinkertaista. Koko "hauska asia" on se, että strlen-toiminto ei ota huomioon rivin lopun merkkiä - "\0". Vaikka määrität sen erikseen saapuvalla rivillä (#define HELLO_STRING "Hei, Habr!\n\0"), se ohitetaan.

Juuri ohjelman suorituksen tuloksen, rivin yläpuolella -> Hei, Habr! siellä on yksityiskohtainen raportti siitä, mistä ja missä arvokas valgrindimme ei pitänyt. Suosittelen, että katsot itse näitä linjoja ja teet omat johtopäätöksesi.

Itse asiassa ohjelman oikea versio näyttää tältä:

#sisältää #sisältää #sisältää #define HELLO_STRING "Hei, Habr!\n" void main() ( char *str = malloc(sizeof(char) * (strlen(HELLO_STRING) + 1)); strcpy(str, HELLO_STRING); printf("->\" t%s", str); vapaa(str); )
Käydään se valgrindin läpi:

$ valgrind --tool=memcheck ./a.out -> Hei, Habr! ==3435== ==3435== KEON YHTEENVETO: ==3435== käytössä poistuttaessa: 0 tavua 0 lohkossa ==3435== keon kokonaiskäyttö: 2 varattua, 2 vapaata, 1 038 tavua varattu ==3435= = ==3435== Kaikki kasalohkot vapautettiin -- vuodot eivät ole mahdollisia ==3435== ==3435== Havaittujen ja estettyjen virheiden määrä, suorita uudelleen komennolla: -v ==3435== VIRHEYHTEENVETO: 0 virhettä 0 kontekstista (suljettu: 0 0:sta)
Loistava. Ei virheitä, +1 tavu varattu muisti auttoi ratkaisemaan ongelman.

Mielenkiintoista on se, että useimmissa tapauksissa sekä ensimmäinen että toinen ohjelma toimivat samalla tavalla, mutta jos sille riville varattu muisti, jolle loppumerkki ei mahtunut, ei nollattu, niin printf()-funktiota tulostettaessa tällainen rivi , tulostaa myös kaikki roskat tämän rivin jälkeen - kaikki tulostetaan, kunnes rivinpäätemerkki tulee printf(:n) tielle.

Tiedät kuitenkin, että (strlen(str) + 1) on tällainen ratkaisu. Meillä on 2 ongelmaa:

  1. Entä jos meidän on varattava muistia esimerkiksi s(n)printf(..)-komennolla luodulle merkkijonolle? Emme tue väitteitä.
  2. Ulkomuoto. Muuttujan ilmoitusrivi näyttää aivan kamalalta. Jotkut kaverit onnistuvat myös kiinnittämään (char *) mallociin, ikään kuin he kirjoittaisivat plussien alle. Ohjelmassa, jossa joudut säännöllisesti käsittelemään merkkijonoja, on järkevää löytää tyylikkäämpi ratkaisu.
Keksitään ratkaisu, joka tyydyttää sekä meitä että valgrindia.

snprintf()

int snprintf(merkki *str, koko_t koko, const char *muoto, ...);- funktio - sprintf:n laajennus, joka muotoilee merkkijonon ja kirjoittaa sen ensimmäisenä argumenttina annettuun osoittimeen. Se eroaa sprintf():stä siinä, että str ei kirjoita tavua suurempaa kuin koossa määritetty.

Funktiolla on yksi mielenkiintoinen ominaisuus - joka tapauksessa se palauttaa luodun merkkijonon koon (ottamatta huomioon rivin loppumerkkiä). Jos merkkijono on tyhjä, palautetaan 0.

Yksi strlenin käyttämisestä kuvaamistani ongelmista liittyy sprintf()- ja snprintf()-funktioihin. Oletetaan, että meidän täytyy kirjoittaa jotain merkkijonoon str. Viimeinen rivi sisältää muiden muuttujien arvot. Kirjauksemme pitäisi olla jotain tällaista:

Char * str = /* varaa muisti tähän */; sprintf(str, "Hei, %s\n", "Habr!");
Herää kysymys: kuinka määrittää, kuinka paljon muistia tulee varata merkkijonolle str?

Char * str = malloc(sizeof(char) * (strlen(str, "Hei, %s\n", "Habr!") + 1)); - se ei tule toimimaan. Strlen()-funktion prototyyppi näyttää tältä:

#sisältää koko_t strlen(const char *s);
const char *s ei tarkoita, että s:lle välitetty merkkijono voi olla vaihtelevamuotoinen merkkijono.

Edellä mainitsemani snprintf()-funktion hyödyllinen ominaisuus auttaa meitä tässä. Katsotaanpa seuraavan ohjelman koodia:

#sisältää #sisältää #sisältää void main() ( /* Koska snprintf() ei ota huomioon rivin loppumerkkiä, lisäämme sen koon tulokseen */ size_t needs_mem = snprintf(NULL, 0, "Hei, %s!\n", "Habr") + sizeof("\0"); char *str = malloc(tarvittava_muisti); snprintf(str, tarvitaan_mem, "Hei, %s!\n", "Habr"); printf("->\t %s", str); vapaa(str); )
Suorita ohjelma valgrindissa:

$ valgrind --tool=memcheck ./a.out -> Hei, Habr! ==4132== ==4132== KEON YHTEENVETO: ==4132== käytössä poistuttaessa: 0 tavua 0 lohkossa ==4132== keon kokonaiskäyttö: 2 varattua, 2 vapaata, 1 041 tavua varattu ==4132= = ==4132== Kaikki kasalohkot vapautettiin -- vuodot eivät ole mahdollisia ==4132== ==4132== Havaittujen ja estettyjen virheiden määrä, suorita uudelleen komennolla: -v ==4132== VIRHEYHTEENVETO: 0 virhettä 0 kontekstista (suljettu: 0 0:sta) $
Loistava. Meillä on argumenttien tuki. Koska välitämme nollan snprintf()-funktion toisena argumenttina, nollaosoittimeen kirjoittaminen ei koskaan aiheuta Seagfaultia. Tästä huolimatta funktio palauttaa silti merkkijonolle vaaditun koon.

Mutta toisaalta meidän piti ottaa käyttöön lisämuuttuja ja suunnittelu

Size_t needs_mem = snprintf(NULL, 0, "Hei, %s!\n", "Habr") + sizeof("\0");
näyttää vielä pahemmalta kuin strlen().

Yleensä + sizeof("\0") voidaan poistaa, jos määrität muotorivin loppuun nimenomaisesti "\0" (size_t needs_mem = snprintf(NULL, 0, "Hei, %s!\n \0 ", "Habr");), mutta tämä ei suinkaan ole aina mahdollista (riippuen merkkijonon käsittelymekanismista, voimme varata ylimääräisen tavun).

Meidän on tehtävä jotain. Mietin hieman ja päätin, että nyt on aika vedota muinaisten viisauteen. Kuvataan makrofunktio, joka kutsuu snprintf()-funktiota tyhjällä osoittimella ensimmäisenä argumenttina ja nulla toisena argumenttina. Ja älkäämme unohtako jonon loppua!

#define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0")
Kyllä, se voi olla joillekin uutinen, mutta C-makrot tukevat vaihtelevaa määrää argumentteja, ja ellipsi kertoo esiprosessorille, että määritetty makrofunktion argumentti (tapauksessamme args) vastaa useita todellisia argumentteja.

Katsotaanpa ratkaisuamme käytännössä:

#sisältää #sisältää #sisältää #define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0") void main() ( char *str = malloc(strsize("Hei, %s\n", "Habr! ")); sprintf(str, "Hei, %s\n", "Habr!"); printf("->\t%s", str); free(str); )
Aloitetaan valgrundista:

$ valgrind --tool=memcheck ./a.out -> Hei, Habr! ==6432== ==6432== KEON YHTEENVETO: ==6432== käytössä poistuttaessa: 0 tavua 0 lohkossa ==6432== keon kokonaiskäyttö: 2 varattua, 2 vapaata, 1 041 tavua varattu ==6432= = ==6432== Kaikki kasalohkot vapautettiin -- vuodot eivät ole mahdollisia ==6432== ==6432== Havaittujen ja estettyjen virheiden määrä, suorita uudelleen komennolla: -v ==6432== VIRHEYHTEENVETO: 0 virhettä 0 kontekstista (suljettu: 0 0:sta)
Kyllä, ei ole virheitä. Kaikki on oikein. Ja valgrind on onnellinen, ja ohjelmoija voi vihdoin mennä nukkumaan.

Mutta lopuksi sanon vielä yhden asian. Jos joudumme varaamaan muistia jollekin merkkijonolle (jopa argumenteilla), se on jo olemassa täysin toimiva ratkaisu.

Puhumme asprintf-funktiosta:

#define _GNU_SOURCE /* Katso feature_test_macros(7) */ #include int asprintf(char ** strp, const char * fmt, ...);
Se ottaa osoittimen merkkijonoon (**strp) ensimmäisenä argumenttinaan ja varaa muistia viittauksesta poistetulle osoittimelle.

Asprintf():llä kirjoitettu ohjelmamme näyttää tältä:

#sisältää #sisältää #sisältää void main() ( char *str; asprintf(&str, "Hei, %s!\n", "Habr"); printf("->\t%s", str); free(str); )
Ja itse asiassa valgrindissa:

$ valgrind --tool=memcheck ./a.out -> Hei, Habr! ==6674== ==6674== KEON YHTEENVETO: ==6674== käytössä poistuttaessa: 0 tavua 0 lohkossa ==6674== keon kokonaiskäyttö: 3 varattua, 3 vapaata, 1 138 tavua varattu ==6674= = ==6674== Kaikki kasalohkot vapautettiin -- vuodot eivät ole mahdollisia ==6674== ==6674== Havaittujen ja estettyjen virheiden määrä, suorita uudelleen komennolla: -v ==6674== VIRHEYHTEENVETO: 0 virhettä 0 kontekstista (suljettu: 0 0:sta)
Kaikki on hyvin, mutta kuten näet, muistia on varattu enemmän, ja nyt niitä on kolme, ei kaksi. Heikosti sulautetuissa järjestelmissä tämän toiminnon käyttö ei ole toivottavaa.
Lisäksi, jos kirjoitamme konsoliin man asprintf, näemme:

VAATIMUSTEN Nämä toiminnot ovat GNU-laajennuksia, eivät C- tai POSIX-kielissä. Ne ovat saatavilla myös *BSD:llä. FreeBSD-toteutus asettaa strp:n arvoon NULL virheen sattuessa.

Tästä se on selvää tämä toiminto saatavilla vain GNU-lähteistä.

Johtopäätös

Lopuksi haluan sanoa, että merkkijonojen kanssa työskentely C:ssä on erittäin monimutkainen aihe, jossa on useita vivahteita. Esimerkiksi "turvallisen" koodin kirjoittamiseen milloin dynaaminen allokointi muisti, on silti suositeltavaa käyttää calloc()-funktiota malloc()-funktion sijaan - calloc täyttää varatun muistin nolilla. Tai käytä muistin varauksen jälkeen memset()-funktiota. Muuten alun perin varatulle muistialueelle sijoitetut roskat voivat aiheuttaa ongelmia virheenkorjauksen aikana ja joskus myös merkkijonon kanssa työskennellessä.

Yli puolet tuntemistani C-ohjelmoijista (useimmat heistä ovat aloittelijoita), jotka ratkaisivat muistin varaamisen merkkijonoille minun pyynnöstäni, teki sen tavalla, joka lopulta johti kontekstivirheisiin. Yhdessä tapauksessa - jopa muistivuotoon (no, henkilö unohti tehdä free(str), sitä ei koskaan tapahdu kenellekään). Itse asiassa tämä sai minut luomaan tämän luomuksen, jonka juuri luit.

Toivon, että tämä artikkeli on hyödyllinen jollekin. Miksi teen kaiken tämän hälinän - mikään kieli ei ole yksinkertaista. Kaikkialla on omat hienovaraisuutensa. Ja mitä enemmän kielen hienouksia osaat, sitä parempi on koodisi.

Uskon, että tämän artikkelin lukemisen jälkeen koodistasi tulee hieman parempi :)
Onnea, Habr!

Linjat. Merkkijonon tulo/lähtö. Muotoiltu I/O. Merkkijonojen käsittely standardi C-kielen funktioilla Työskentely muistin kanssa.

1.1. Merkkijonojen ilmoittaminen ja alustus.

Merkkijono on merkkijono, joka päättyy tyhjään merkkiin \0. Merkkijono ilmoitetaan tavalliseksi merkkijonoksi, esim.

char s1; // merkkijono yhdeksän merkkiä pitkä

char *s2; // osoitin merkkijonoon

Ero osoittimien s1 ja s2 välillä on se, että osoitin s1 on nimetty vakio ja osoitin s2 on muuttuja.

Merkkijonovakiot on suljettu lainausmerkkeihin, toisin kuin merkit, jotka on suljettu lainausmerkkeihin. Esimerkiksi,

"Tämä on merkkijono."

Merkkijonovakion pituus ei saa ylittää 509 merkkiä standardin mukaan. Monet toteutukset sallivat kuitenkin pidemmät merkkijonopituudet.

Merkkijonoja alustettaessa on parempi olla määrittämättä taulukon kokoa, vaan kääntäjä tekee tämän laskemalla merkkijonon pituuden ja lisäämällä siihen yhden. Esimerkiksi,

char s1 = "Tämä on merkkijono.";

C-ohjelmointikielessä merkkijonojen kanssa työskentelyyn on olemassa suuri määrä toimintoja, joiden prototyypit on kuvattu otsikkotiedostoissa stdlib.h ja string.h. Näiden toimintojen käyttöä käsitellään seuraavissa kappaleissa.

1.2. Merkkijonon tulo/lähtö.

Käytä funktiota syöttääksesi merkkijonon konsolista

char* gets(char *str);

joka kirjoittaa merkkijonon osoitteeseen str ja palauttaa syötetyn merkkijonon osoitteen. Funktio pysäyttää syötteen, jos se kohtaa merkin '\n' tai EOF (tiedoston loppu). Rivinvaihtomerkkiä ei kopioida. Nolla tavu sijoitetaan lukurivin loppuun. Jos onnistuu, funktio palauttaa osoittimen luettavalle riville, ja jos se ei onnistu, NULL.

Jos haluat tulostaa merkkijonon konsoliin, käytä vakiotoimintoa

int laittaa (const char *s);

joka onnistuessaan palauttaa ei-negatiivisen luvun, ja jos se ei onnistu, palauttaa EOF:n.

Gets and puts -funktioiden prototyypit on kuvattu stdio.h-otsikkotiedostossa.

#sisältää

printf("Syötemerkkijono: ");

1.3. Muotoiltu I/O.

Käytä toimintoa muotoiltujen tietojen syöttämiseen konsolista

int scanf (const char *muoto, ...);

joka onnistuessaan palauttaa luettujen tietojen yksiköiden määrän, ja jos se ei onnistu, palauttaa EOF:n. Muoto-parametrin on osoitettava muotoiltavaan merkkijonoon, joka sisältää syöttömuotomääritykset. Muotomerkkijonoa seuraavien argumenttien määrän ja tyyppien on vastattava muotomerkkijonossa määritettyjen syöttömuotojen määrää ja tyyppejä. Jos tämä ehto ei täyty, funktion tulos on arvaamaton.

Välilyönti, "\t" tai "\n" muotomerkkijonossa kuvaa yhtä tai useampaa tyhjää merkkiä syöttövirrassa, jotka sisältävät seuraavat merkit: välilyönti, '\t', '\n', '\v', '\f'. Scanf-toiminto ohittaa tyhjät merkit syöttövirrassa.

Muotomerkkijonon kirjaimelliset merkit, lukuun ottamatta %-merkkiä, edellyttävät, että syöttövirrassa on täsmälleen samat merkit. Jos tällaista merkkiä ei ole, scanf-toiminto lopettaa syöttämisen. Scanf-toiminto ohittaa kirjaimelliset merkit.

Yleisesti ottaen syöttömuodon määritys näyttää tältä:

%[*] [leveys] [muokkaimet] tyyppi

Symboli '*' tarkoittaa puutetta syötettäessä tämän spesifikaation määrittelemään kenttään;

- "leveys" määrittää tämän määrityksen mukaisesti syötettävien merkkien enimmäismäärän;

Tyyppi voi ottaa seuraavat arvot:

c – merkkijono,

s – merkkijono, rivit erotetaan tyhjillä merkeillä,

d – etumerkillinen kokonaisluku 10 s/s,

i – etumerkillinen kokonaisluku, numerojärjestelmä riippuu kahdesta ensimmäisestä numerosta,

u – etumerkitön kokonaisluku nopeudella 10 s/s,

o – etumerkitön kokonaisluku 8 s/s,

x, X – etumerkitön kokonaisluku nopeudella 16 s/s,

e, E, f, g, G – kelluva luku,

p - osoitin osoittimeen,

n – osoitin kokonaislukuun,

[…] – joukko skannattuja merkkejä, esimerkiksi .

Jälkimmäisessä tapauksessa vain hakasulkeissa olevat merkit syötetään syöttövirrasta. Jos ensimmäinen merkki hakasulkeissa on '^', syötetään vain ne merkit, jotka eivät ole taulukossa. Taulukon merkkialue määritetään '-'-symbolilla. Kun syötät merkkejä, myös merkkijonon alussa olevat tyhjät merkit ja lopussa oleva nollatavu syötetään.

Modifioijat voivat ottaa seuraavat arvot:

h – lyhyt kokonaisluku,

l, L – pitkä kokonaisluku tai kelluva,

ja niitä käytetään vain kokonaislukuihin tai kelluviin lukuihin.

SISÄÄN seuraava esimerkki näyttää skannaustoiminnon käyttövaihtoehdot. Huomaa, että muotomääritteen, joka alkaa kelluvan numeron syötöstä, edeltää välilyönti.

#sisältää

printf("Syötä kokonaisluku: ");

scanf("%d", &n);

printf("Syötä tupla: ");

scanf(" %lf", &d);

printf("Syötä merkki: ");

scanf(" %c", &c);

printf("Syötä merkkijono: ");

scanf(" %s", &s);

Huomaa, että tässä ohjelmassa liukuluku alustetaan. Tämä tehdään niin, että kääntäjä sisältää kirjaston, joka tukee kelluvien lukujen käyttöä. Jos tätä ei tehdä, kelluvaa lukua syötettäessä tapahtuu virhe ajon aikana.

Käytä toimintoa tietojen muotoillulle tulostukselle konsoliin

int printf (const char *muoto, ...);

joka onnistuessaan palauttaa tulostettujen datayksiköiden määrän, ja jos se ei onnistu, palauttaa EOF:n. Muotoparametri on muotomerkkijono, joka sisältää tulostusmuotojen määritykset. Muotomerkkijonoa seuraavien argumenttien määrän ja tyyppien on vastattava muotomerkkijonossa määritettyjen tulostusmuotomäärittelyjen määrää ja tyyppejä. Yleisesti tulostusmuodon määritys näyttää tältä:

%[liput] [leveys] [.tarkkuus] [muokkaimet] tyyppi

- "liput" ovat erilaisia ​​symboleja, jotka määrittävät tulostusmuodon;

- "leveys" määrittelee tulostettavien merkkien vähimmäismäärän tämän määrityksen mukaisesti;

- ".tarkkuus" määrittää näytettävien merkkien enimmäismäärän;

- "muuttajat" määrittävät argumenttien tyypin;

- 'type' määrittää argumentin tyypin.

Etumerkillisten kokonaislukujen tulostamiseen käytetään seuraavaa tulostusmuotoa:

%[-] [+ | välilyönti] [leveys] [l] d

- – kohdistus vasemmalle, oletusarvo – oikea;

+ – '+'-merkki tulee näkyviin. Huomaa, että negatiivisia lukuja'-'-merkki näkyy aina;

‘välilyönti’ – merkin kohdalla näytetään välilyönti;

d – int-tietotyyppi.

Jos haluat tulostaa etumerkittömiä kokonaislukuja, käytä seuraavaa tulostusmuotoa:

%[-] [#] [leveys] [l]

# – alku0 tulostetaan numeroille 8 c/c tai alku 0x tai 0X numeroille 16 c/c,

l – pitkä tietotyypin muuntaja;

u – kokonaisluku 10c/c:ssä,

o – kokonaisluku 8 c/c:ssä,

x, X – kokonaisluku 16 c/c.

Seuraavaa tulostusmuotoa käytetään liukulukujen näyttämiseen:

%[-] [+ | välilyönti] [leveys] [.tarkkuus]

"tarkkuus" - ilmaisee desimaalipilkun jälkeisten numeroiden määrän muodoissa f, e ja E tai merkitsevien numeroiden lukumäärän muodoissa g ja G. Numerot pyöristetään. Oletustarkkuus on kuusi desimaalilukua;

f – kiinteän pisteen numero,

e – eksponenttimuodossa oleva luku, eksponenttia merkitään kirjaimella "e",

E – eksponenttimuodossa oleva luku, eksponenttia merkitään kirjaimella "E",

g – lyhin f- tai g-muodoista,

G – lyhin f- tai G-muodoista.

printf ("n = %d\n f = %f\n e = %e\n E = %E\n f = %.2f", -123, 12,34, 12,34, 12,34, 12,34);

// tulostaa: n = 123 f = 12.340000 e = 1.234000e+001 E = 1.234000E+001 f = 12.34

1.4. Merkkijonojen muotoilu.

Vaihtoehtoja on scanf-toiminnot ja printf, jotka on suunniteltu muotoilemaan merkkijonoja ja joita kutsutaan vastaavasti sscanf ja sprintf.

int sscanf (const char *str, const char *muoto, ...);

lukee tiedot str määrittämästä merkkijonosta muodon määrittämän merkkijonon mukaan. Jos onnistuu, palauttaa luettujen tietojen määrän, ja jos ei onnistu, palauttaa EOF:n. Esimerkiksi,

#sisältää

char str = "a 10 1,2 merkkijono Ei syöttöä";

sscanf(str, "%c %d %lf %s", &c, &n, &d, s);

printf("%c\n", c); // tulostaa: a

printf("%d\n", n); // tulostaa: 10

printf("%f\n", d); // tulostaa: 1.200000

printf("%s\n", s); // tulostaa: String

int sprintf (char *puskuri, const char *muoto, ...);

muotoilee merkkijonon format-parametrin määrittämän muodon mukaisesti ja kirjoittaa tuloksen merkkijonopuskuriin. Funktio palauttaa merkkijonopuskuriin kirjoitettujen merkkien määrän, ei sisällä lopettavaa nollatavua. Esimerkiksi,

#sisältää

char str = "c = %c, n = %d, d = %f, s = %s";

char s = "Tämä on merkkijono.";

sprintf(puskuri, str, c, n, d, s);

printf("%s\n", puskuri); // tulostaa: c = c, n = 10, d = 1,200000, s = Tämä on merkkijono

1.5. Muunna merkkijonot numeerisiksi tiedoiksi.

Merkkijonojen numeerisiksi tiedoiksi muuntamisen funktioiden prototyypit on annettu stdlib.h-otsikkotiedostossa, joka on sisällytettävä ohjelmaan.

Jos haluat muuntaa merkkijonon kokonaisluvuksi, käytä funktiota

int atoi (const char *str);

char *str = "-123";

n = atoi(str); // n = -123

Jos haluat muuntaa merkkijonon pitkäksi kokonaisluvuksi, käytä funktiota

pitkä int atol (const char *str);

joka onnistuessaan palauttaa kokonaisluvun, johon merkkijono str muunnetaan, ja jos se ei onnistu, palauttaa 0:n.

char *str = "-123";

n = atol(str); // n = -123

Jos haluat muuntaa merkkijonon kaksoisluvuksi, käytä funktiota

double atof(const char *str);

joka onnistuessaan palauttaa kelluvan luvun double, johon merkkijono str muunnetaan, ja jos se ei onnistu, palauttaa 0:n.

char *str = "-123.321";

n = atof(str); // n = -123,321

Seuraavat toiminnot suorittavat samanlaisia ​​toimintoja kuin atoi, atol, atof, mutta tarjoavat edistyneempiä toimintoja.

pitkä int strtol (const char *str, char **endptr, int base);

muuntaa merkkijonon str pitkäksi int-luvuksi, jonka se palauttaa. Tämän toiminnon parametreilla on seuraavat tarkoitukset.

Jos kantaluku on 0, muunnos riippuu str:n kahdesta ensimmäisestä merkistä:

Jos ensimmäinen merkki on numero 1-9, niin luvun oletetaan olevan 10 c/c;

Jos ensimmäinen merkki on numero 0 ja toinen merkki on numero 1 - 7, niin luvun oletetaan olevan 8 c/c;

Jos ensimmäinen merkki on 0 ja toinen on 'X' tai 'x', niin numeron oletetaan olevan 16 c/c.

Jos kanta on luku väliltä 2 ja 36, ​​tämä arvo on lukujärjestelmän kanta, ja mikä tahansa numerojärjestelmän ulkopuolella oleva merkki lopettaa muuntamisen. Perusnumerojärjestelmissä 11–36, symboleja 'A' - 'Z' tai 'a' - 'z' käytetään edustamaan numeroita.

Endptr-argumentin arvon asettaa strtol-funktio. Tämä arvo sisältää osoittimen merkkiin, joka pysäytti merkkijonon str muuntamisen. Strtol-funktio palauttaa muunnetun luvun, jos se onnistuu, ja 0:n, jos se ei onnistu.

n = strtol ("12a", &p, 0);

printf("n = %ld, %stop = %c, n, *p); // n = 12, stop = a

n = strtol("012b", &p, 0);

printf("n = %ld, %stop = %c, n, *p); // n = 10, stop = b

n = strtol ("0x12z", &p, 0);

printf("n = %ld, %stop = %c, n, *p); // n = 18, stop = z

n = strtol ("01117", &p, 0);

printf("n = %ld, %stop = %c, n, *p); // n = 7, stop = 7

unsigned long int strtol (const char *str, char **endptr, int base);

toimii samalla tavalla kuin strtol-funktio, mutta muuntaa symbolinen esitys numerot numerotyyppiin unsigned long int.

double strtod (const char *str, char **endptr);

Muuntaa luvun symbolisen esityksen kaksinkertaiseksi.

Kaikki tässä kappaleessa luetellut toiminnot lakkaavat toimimasta, kun ne kohtaavat ensimmäisen merkin, joka ei sovi kyseisen numeron muotoon.

Lisäksi, jos luvun merkkiarvo ylittää vastaavan tietotyypin hyväksyttävien arvojen alueen, funktiot atof, strtol, strtoul, strtod asettavat errno-muuttujan arvoksi ERANGE. Errno-muuttuja ja ERANGE-vakio määritellään math.h-otsikkotiedostossa. Tässä tapauksessa atof- ja strtod-funktiot palauttavat arvon HUGE_VAL, strtol-funktio palauttaa arvon LONG_MAX tai LONG_MIN ja strtoul-funktio palauttaa ULONG_MAX-arvon.

Epätyypillisiä toimintoja itoa, ltoa, utoa, ecvt, fcvt ja gcvt voidaan käyttää numeerisen tiedon muuntamiseen merkkijonoiksi. Mutta on parempi käyttää tavallista sprintf-toimintoa näihin tarkoituksiin.

1.6. Standarditoiminnot merkkijonojen kanssa työskentelyyn.

Tässä osiossa käsitellään toimintoja merkkijonojen käsittelyyn, joiden prototyypit on kuvattu otsikkotiedostossa string.h.

1. Merkkijonojen vertailu. Funktioita strcmp ja strncmp käytetään merkkijonojen vertailuun.

int strcmp (const char *str1, const char * str2);

vertaa leksikografisesti merkkijonoja str1, str2 ja palauttaa -1, 0 tai 1, jos str1 on vastaavasti pienempi, yhtä suuri tai suurempi kuin str2.

int strncmp (vakiomerkki *str1, vakiomerkki *str2, koko_t n);

vertaa leksikografisesti enintään n ensimmäistä merkkiä merkkijonoista str1 ja str2. Funktio palauttaa arvon -1, 0 tai 1, jos str1:n ensimmäiset n merkkiä ovat vastaavasti pienempiä, yhtä suuria tai suurempia kuin str2:n ensimmäiset n merkkiä.

// esimerkki merkkijonojen vertailusta

#sisältää

#sisältää

char str1 = "aa bb";

char str2 = "aa aa";

char str3 = "aa bb cc";

printf("%d\n", strcmp(str1, str3)); // tulostaa: -1

printf("%d\n", strcmp(str1, str1)); // tulostaa: -0

printf("%d\n", strcmp(str1, str2)); // tulostaa: 1

printf("%d\n", strncmp(str1, str3, 5)); // tulostaa: 0

2. Viivojen kopioiminen. Strcpy- ja strncpy-funktioita käytetään merkkijonojen kopioimiseen.

char * strcpy (char * str1, const char * str2);

kopioi merkkijonon str2 merkkijonoon str1. Koko merkkijono str2 kopioidaan, mukaan lukien päättyvä nollatavu. Funktio palauttaa osoittimen str1:een. Jos viivat menevät päällekkäin, tulos on arvaamaton.

char *strncpy (merkki *str1, const char *str2, koko_t n);

kopioi n merkkiä merkkijonosta str2 merkkijonoon str1. Jos str2 sisältää vähemmän kuin n merkkiä, viimeinen nollatavu kopioidaan niin monta kertaa kuin on tarpeen str2:n laajentamiseksi n merkkiin. Funktio palauttaa osoittimen merkkijonoon str1.

char str2 = "Kopioi merkkijono.";

strcpy(str1, str2);

printf(str1); // tulostaa: Kopioi merkkijono.

4. Kytkentänauhat. Funktioita strcat ja strncat käytetään ketjuttamaan merkkijonoja yhdeksi merkkijonoksi.

char* strcat (char *str1, const char *str2);

liittää merkkijonon str2 merkkijonoon str1, jolloin merkkijonon str1 lopussa oleva nollatavu poistetaan. Funktio palauttaa osoittimen merkkijonoon str1.

char* strncat (char *str1, const char *str2, koko_t n);

lisää n merkkiä merkkijonosta str2 merkkijonoon str1, jolloin merkkijonon str1 lopussa oleva nollatavu poistetaan. Funktio palauttaa osoittimen merkkijonoon str1. jos merkkijonon str2 pituus on pienempi kuin n, vain merkkijonoon str2 sisältyvät merkit lisätään. Merkkijonojen ketjuttamisen jälkeen str1:een lisätään aina nollatavu. Funktio palauttaa osoittimen merkkijonoon str1.

#sisältää

#sisältää

char str1 = "merkkijono";

char str2 = "katenaatio";

char str3 = "Kyllä Ei";

strcat(str1, str2);

printf("%s\n", str1); // tulostaa: String catenation

strncat(str1, str3, 3);

printf("%s\n", str1); // tulostaa: String catenation Kyllä

5. Etsi merkkijonosta. Voit etsiä merkkiä merkkijonosta käyttämällä funktioita strchr, strrchr, strspn, strcspn ja strpbrk.

char* strchr (const char *str, int c);

etsii c:llä määritetyn merkin ensimmäistä esiintymää merkkijonosta str. Jos onnistuu, funktio palauttaa osoittimen ensimmäiseen löydettyyn merkkiin, ja jos se ei onnistu, NULL.

char* strrchr (const char *str, int c);

etsii c:llä määritetyn merkin viimeistä esiintymää merkkijonosta str. Jos onnistuu, funktio palauttaa osoittimen viimeiseen löydettyyn merkkiin, ja jos se ei onnistu, NULL.

#sisältää

#sisältää

char str = "Char search";

printf("%s\n", strchr(str, "r")); // tulostaa: r haku

printf("%s\n", strrchr(str, "r")); // tulostaa: rch

koko_t strspn (vakiomerkki *str1, vakiomerkki *str2);

palauttaa str1:n ensimmäisen merkin indeksin, joka ei ole str2:ssa.

koko_t strcspn (vakiomerkki *str1, vakiomerkki *str2);

palauttaa str1:n ensimmäisen merkin indeksin, joka esiintyy str2:ssa.

char str = "123 abc";

printf ("n = %d\n", strspn (str, "321"); // tulostaa: n = 3

printf ("n = %d\n", strcspn (str, "cba"); // tulostaa: n = 4

char* strpbrk (const char *str1, const char *str2);

löytää ensimmäisen merkin merkkijonosta str1, joka on yhtä suuri kuin jokin merkkijonon str2 merkeistä. Jos onnistuu, funktio palauttaa osoittimen tähän merkkiin, ja jos se ei onnistu, NULL.

char str = "123 abc";

printf("%s\n", strpbrk(str, "bca")); // tulostaa: abc

6. Merkkijonojen vertailu. Strstr-funktiota käytetään merkkijonojen vertailuun.

char* strstr (vakiomerkki *str1, const char *str2);

löytää str2:n ensimmäisen esiintymän (ilman lopussa olevaa nollatavua) str1:stä. Jos onnistuu, funktio palauttaa osoittimen löydettyyn osamerkkijonoon, ja jos se ei onnistu, NULL. Jos str1-osoitin osoittaa nollapituiseen merkkijonoon, funktio palauttaa str1-osoittimen.

char str = "123 abc 456;

printf ("%s\n", strstr (str, "abc"); // tulosta: abc 456

7. Merkkijonon jäsentäminen tokeneiksi. Strtok-funktiota käytetään jäsentämään merkkijono tokeneiksi.

char* strtok (char *str1, const char *str2);

palauttaa osoittimen merkkijonon str1 seuraavaan merkkiin (sanaan), jossa merkkien erottimet ovat merkkijonon str2 merkkejä. Jos merkkejä ei ole enää, funktio palauttaa NULL-arvon. Ensimmäisessä strtok-funktion kutsussa str1-parametrin on osoitettava merkkijonoon, joka on tokenoitu, ja myöhemmissä kutsuissa tämän parametrin on oltava NULL. Kun merkki on löydetty, strtok-funktio kirjoittaa nollatavun tämän merkin jälkeen erottimen tilalle.

#sisältää

#sisältää

char str = "12 34 ab cd";

p = strtok(str, " ");

printf("%s\n", p); // tulostaa arvot sarakkeessa: 12 34 ab cd

p = strtok(NULL, " ");

8. Merkkijonon pituuden määrittäminen. Strlen-funktiota käytetään määrittämään merkkijonon pituus.

koko_t strlen (const char *str);

palauttaa merkkijonon pituuden jättäen huomioimatta viimeisen nollatavun. Esimerkiksi,

char str = "123";

printf("len = %d\n", strlen(str)); // tulostaa: len = 3

1.7. Toiminnot muistin kanssa työskentelemiseen.

Otsikkotiedostossa string.h kuvataan myös muistilohkojen kanssa työskentelyyn liittyviä toimintoja, jotka ovat samanlaisia ​​kuin vastaavat merkkijonojen kanssa työskentelyn toiminnot.

void* memchr (const void *str, int c, size_t n);

etsii c:n määrittämän merkin ensimmäistä esiintymää merkkijonon str n tavusta.

int memcmp (const void *str1, const void *str2, koko_t n);

vertaa merkkijonojen str1 ja str2 ensimmäisiä n tavua.

void* memcpy (const void *str1, const void *str2, koko_t n);

kopioi ensimmäiset n tavua merkkijonosta str1 merkkijonoon str2.

void* memmove (const void *str1, const void *str2, koko_t n);

kopioi ensimmäiset n tavua str1:stä str2:een varmistaen, että päällekkäisiä merkkijonoja käsitellään oikein.

void* memset (const void *str, int c, size_t n);

kopioi c:n määrittämän merkin str:n ensimmäisiin n tavuun.

34

--- C#-opas --- Jouset

Tavallisen ohjelmoinnin näkökulmasta merkkijono merkkijonotietotyyppi on yksi tärkeimmistä C#:ssa. Tämä tyyppi määrittelee ja tukee merkkijonoja. Monissa muissa ohjelmointikielissä merkkijono on merkkijono. Ja C#:ssa merkkijonot ovat objekteja. Siksi merkkijonotyyppi on viitetyyppi.

Rakennusnauhat

Yksinkertaisin tapa muodostaa merkkijono on käyttää merkkijonoliteraalia. Esimerkiksi sisään seuraava rivi koodia, merkkijonoviittausmuuttujalle str määritetään viittaus merkkijonoliteraaliin:

String str = "Esimerkkijono";

Tässä tapauksessa muuttuja str alustetaan merkkijonolla "Example String". String-tyyppinen objekti voidaan luoda myös char-tyypin taulukosta. Esimerkiksi:

Char chararray = ("e", "x", "a", "m", "p", "l", "e"); merkkijono str = uusi merkkijono(chararray);

Kun merkkijonoobjekti on luotu, sitä voidaan käyttää missä tahansa, missä tarvitset lainausmerkeissä olevan tekstijonon.

Jousen pysyvyys

Kummallista kyllä, merkkijonotyyppisen objektin sisältöä ei voi muuttaa. Tämä tarkoittaa, että kun merkkijono on luotu, sitä ei voi muuttaa. Mutta tämä rajoitus edistää merkkijonojen tehokkaampaa toteutusta. Siksi tämä ilmeisen ilmeinen haitta muuttuu itse asiassa eduksi. Jos siis tarvitaan merkkijono muunnelmana olemassa olevasta merkkijonosta, niin tätä tarkoitusta varten tulee luoda uusi merkkijono, joka sisältää kaikki tarvittavat muutokset. Ja koska käyttämättömät merkkijonoobjektit kerätään automaattisesti roskiksi, sinun ei tarvitse edes huolehtia tarpeettomien merkkijonojen kohtalosta.

On kuitenkin korostettava, että muuttujaviittaukset merkkijonoihin (eli merkkijonotyyppisiin objekteihin) voivat muuttua, ja siksi ne voivat viitata toiseen objektiin. Mutta itse merkkijonoobjektin sisältö ei muutu sen luomisen jälkeen.

Katsotaanpa esimerkkiä:

Static void addNewString() ( string s = "Tämä on minun vetoni"; s = "Tämä on uusi veto"; )

Käännetään sovellus ja ladataan tuloksena oleva kokoonpano ildasm.exe-apuohjelmaan. Kuvassa näkyy CIL-koodi, joka luodaan void addNewString() -metodille:

Huomaa, että ldstr-operaatiokoodiin (merkkijonokuormitukseen) on useita kutsuja. Tämä CIL-ldstr-operaatiokoodi lataa uuden merkkijono-objektin hallittuun kasaan. Tämän seurauksena edellinen objekti, joka sisälsi arvon "Tämä on minun aivohalvaukseni", lopulta kerätään roskat.

Työskentely Stringsin kanssa

Luokassa System.String tarjotaan joukko menetelmiä merkkitietojen pituuden määrittämiseksi, alimerkkijonon etsimiseksi nykyisestä merkkijonosta, merkkien muuntamisesta isoista pieniksi ja päinvastoin jne. Seuraavaksi tarkastelemme tätä luokkaa tarkemmin.

Kenttä-, indeksi- ja merkkijonoluokan ominaisuus

String-luokka määrittää yhden kentän:

Julkinen staattinen vain luku -merkkijono Tyhjä;

Empty-kenttä tarkoittaa tyhjää merkkijonoa, ts. merkkijono, joka ei sisällä merkkejä. Tämä eroaa tyhjästä merkkijonoviittauksesta, joka tehdään yksinkertaisesti olemattomaan objektiin.

Lisäksi String-luokka määrittää yhden vain luku -hakemiston:

Julkinen merkki tämä ( hanki; )

Tämän indeksoijan avulla voit saada merkin tietyllä indeksillä. Merkkijonojen, kuten taulukoiden, indeksointi alkaa nollasta. Merkkijonoobjektit ovat pysyviä eivätkä muutu, joten on järkevää, että String-luokka tukee vain luku -indeksiä.

Lopuksi String-luokka määrittelee yhden vain luku -ominaisuuden:

Julkinen int pituus ( get; )

Pituus-ominaisuus palauttaa merkkijonon merkkien määrän. Alla oleva esimerkki näyttää indeksoijan ja Length-ominaisuuden käytön:

Järjestelmän käyttö; class Esimerkki ( staattinen void Main() ( string str = "Yksinkertainen merkkijono"; // Hae merkkijonon pituus ja rivin kuudes merkki indeksointityökalulla Console.WriteLine("Merkinjonon pituus on (0), 6. merkki on "(1)"" , str.Length, str); ) )

Merkkijonoluokan operaattorit

String-luokka ylikuormittaa seuraavat kaksi operaattoria: == ja !=. ==-operaattoria käytetään kahden merkkijonon tasa-arvon testaamiseen. Kun ==-operaattoria käytetään objektiviittauksiin, se tyypillisesti testaa, tehdäänkö molemmat viittaukset samaan objektiin. Ja kun ==-operaattoria käytetään viittauksiin String-tyypin objekteihin, itse merkkijonojen sisältöä verrataan tasa-arvoon. Sama koskee !=-operaattoria. Kun sitä käytetään viittauksiin String-tyypin objekteihin, itse merkkijonojen sisältöä verrataan epäyhtälön suhteen. Muut relaatiooperaattorit, mukaan lukien =, vertaavat kuitenkin viittauksia String-tyypin objekteihin samalla tavalla kuin viittauksia muun tyyppisiin objekteihin. Ja jotta voit tarkistaa, onko yksi merkkijono suurempi kuin toinen, sinun tulee kutsua String-luokassa määritettyä Compare()-metodia.

Kuten tulee selväksi, monet merkkijonojen vertailut perustuvat kulttuuritietoon. Mutta tämä ei koske ==- ja !=-operaattoreita. Loppujen lopuksi he vain vertaavat merkkijonojen järjestysarvoja. (Toisin sanoen ne vertaavat sellaisten merkkien binääriarvoja, joita ei ole muokattu kulttuurisilla normeilla, eli aluestandardeilla.) Siksi nämä operaattorit suorittavat merkkijonovertailuja isot ja kulttuurit huomioimatta.

Merkkijonoluokan menetelmät

Seuraavassa taulukossa on lueteltu joitakin tämän luokan mielenkiintoisimmista menetelmistä ryhmiteltynä tarkoituksen mukaan:

Keinot työskennellä merkkijonojen kanssa
Menetelmä Rakenne ja ylikuormitukset Tarkoitus
Merkkijonojen vertailu
vertailla() public static int Vertaa(merkkijono strA, merkkijono strB)

Julkinen staattinen int Vertaa (merkkijono strA, merkkijono strB, bool ignoreCase)

Julkinen staattinen int Vertaa (merkkijono strA, merkkijono strB, merkkijonovertailutyyppi)

Julkinen staattinen int Vertaa (merkkijono strA, merkkijono strB, bool ignoreCase, CultureInfo kulttuuri)

Staattinen menetelmä, vertaa merkkijonoa strA merkkijonoon strB. Palauttaa positiivisen arvon, jos strA on suurempi kuin strB; negatiivinen, jos strA on pienempi kuin strB; ja nolla, jos merkkijonot strA ja strB ovat yhtä suuret. Vertailut tehdään rekisterin ja kulttuurin perusteella.

Jos ignoreCase arvioi arvoon tosi, vertailu ei ota huomioon eroja isojen ja pienten kirjainten välillä. Muuten nämä erot huomioidaan.

CompleteType-parametri määrittää, miten merkkijonoja verrataan. CultureInfo-luokka määritellään System.Globalization-nimiavaruudessa.

julkinen staattinen int Vertaa(merkkijono strA, int indeksiA, merkkijono strB, int indeksiB, int pituus)

Julkinen staattinen int Vertaa(merkkijono strA, int indeksiA, merkkijono strB, int indeksiB, int pituus, bool ignoreCase)

Julkinen staattinen int Vertaa(merkkijono strA, int indeksiA, merkkijono strB, int indeksiB, int pituus, merkkijonovertailutyyppi)

Julkinen staattinen int Vertaa(merkkijono strA, int indexA, string strB, int indexB, int pituus, bool ignoreCase, CultureInfo kulttuuri)

Vertaa merkkijonojen strA ja strB osia. Vertailu alkaa merkkijonoelementeillä strA ja strB ja sisältää pituusparametrin määrittämän määrän merkkejä. Menetelmä palauttaa positiivisen arvon, jos osa merkkijonosta strA on suurempi kuin osa merkkijonosta strB; negatiivinen arvo, jos osa merkkijonosta strA on pienempi kuin osa merkkijonosta strB; ja nolla, jos verrattavien merkkijonojen strA ja strB osat ovat yhtä suuret. Vertailut tehdään rekisterin ja kulttuurin perusteella.

VertaaOrdinaal() public static int VertaaOrdinaal(merkkijono strA, merkkijono strB)

Julkinen staattinen int VertaaOrdinaal(merkkijono strA, int indexA, string strB, int indexB, int count)

Toimii samoin kuin Compare()-menetelmä, mutta ei ota huomioon paikallisia asetuksia

Vertaa() julkinen int Vertaa(objektiarvo)

Vertaa kutsuvaa merkkijonoa arvoobjektin merkkijonoesitykseen. Palauttaa positiivisen arvon, jos kutsuva merkkijono on suurempi kuin arvo; negatiivinen, jos kutsuva merkkijono on pienempi kuin arvo; ja nolla, jos verratut merkkijonot ovat yhtä suuret

julkinen int Vertaa(merkkijono strB)

Vertaa kutsuvaa merkkijonoa merkkijonoon strB

Yhtä kuin () julkinen ohitus bool on yhtä kuin (objektiobjekti)

Palauttaa totuusarvon tosi, jos kutsuva merkkijono sisältää saman merkkijonon kuin obj:n merkkijonoesitys. Suorittaa kirjainkoon erottelun, mutta kulttuurisesti välinpitämättömän järjestysvertailun

public bool on yhtä kuin (merkkijonoarvo)

Julkinen bool on yhtä kuin (merkkijonoarvo, merkkijonovertailutyyppi)

Palauttaa loogisen arvon tosi, jos kutsuva merkkijono sisältää saman merkkijonon kuin merkkijonon arvo. Suoritetaan järjestysvertailu, jossa kirjainkoko on herkkä, mutta ei kulttuurisesti herkkä. CompleteType-parametri määrittää, miten merkkijonoja verrataan

julkinen staattinen bool on yhtä kuin (merkkijono a, merkkijono b)

Julkinen staattinen bool on yhtä kuin (merkkijono a, merkkijono b, merkkijonovertailutyyppi)

Palauttaa loogisen arvon tosi, jos merkkijono a sisältää saman merkkijonon kuin merkkijono b . Suoritetaan järjestysvertailu, jossa kirjainkoko on herkkä, mutta ei kulttuurisesti herkkä. CompleteType-parametri määrittää, miten merkkijonoja verrataan

Merkkijonojen ketjuttaminen (yhteys).
Concat() julkinen staattinen merkkijono Concat(merkkijono str0, merkkijono str1);

julkinen staattinen merkkijono Concat(params merkkijonoarvot);

Yhdistää yksittäiset merkkijonot yhdeksi merkkijonoksi (ketjutus)
Hae merkkijonosta
Sisältää() julkinen bool Sisältää (merkkijonoarvo) Menetelmä, jonka avulla voit määrittää, sisältääkö merkkijono tietyn alimerkkijonon (arvon)
Alkaa() julkinen bool Alkaa(merkkijonoarvo)

Julkinen bool StartsWith(merkkijonoarvo, merkkijonovertailutyyppi)

Palauttaa loogisen arvon tosi, jos kutsuva merkkijono alkaa alimerkkijonoarvolla. Muussa tapauksessa looginen arvo false palautetaan. Parametri vertailuType määrittää tietyn tavan suorittaa haku

Loppuu() julkinen bool EndsWith(merkkijonoarvo)

Julkinen bool EndsWith(merkkijonoarvo, merkkijonovertailutyyppi)

Palauttaa loogisen arvon tosi, jos kutsumerkkijono päättyy osamerkkijonon arvoon. Muussa tapauksessa palauttaa loogisen arvon false. Parametri vertailuType määrittää tietyn hakutavan

Sisällysluettelo() julkinen int IndexOf(merkin arvo)

Julkinen int IndexOf(merkkijonoarvo)

Etsii ensimmäisen esiintymän tietyn alimerkkijonon tai merkin merkkijonosta. Jos etsittyä merkkiä tai osamerkkijonoa ei löydy, palautetaan arvo -1.

julkinen int IndexOf(merkin arvo, int aloitusindeksi)

Julkinen int IndexOf(merkkijonoarvo, int aloitusindeksi)

Julkinen int IndexOf(merkin arvo, int aloitusindeksi, int count)

Julkinen int IndexOf(merkkijonoarvo, int aloitusindeksi, int count)

Palauttaa kutsuvan merkkijonon merkin tai alimerkkijonon arvon ensimmäisen esiintymän indeksin. Haku alkaa startIndexin määrittämästä elementistä ja kattaa lukumäärän määrittämän elementin määrän (jos määritetty). Metodi palauttaa -1, jos etsittyä merkkiä tai osamerkkijonoa ei löydy

LastIndexOf() Ylikuormitetut versiot ovat samanlaisia ​​kuin IndexOf()-menetelmä

Sama kuin IndexOf, mutta etsii merkin tai alimerkkijonon viimeisen esiintymän, ei ensimmäistä

IndexOfAny() julkinen int IndexOfAny(char anyOf)

Julkinen int IndexOfAny(char anyOf, int startIndex)

Julkinen int IndexOfAny(char anyOf, int startIndex, int count)

Palauttaa minkä tahansa kutsuvan merkkijonon anyOf-taulukon merkin ensimmäisen esiintymän indeksin. Haku alkaa startIndexin määrittämästä elementistä ja kattaa lukumäärän (jos määritetty) määrittämän elementin määrän. Metodi palauttaa -1, jos mitään merkkiä anyOf-taulukosta ei löydy. Etsintä suoritetaan säännöllisin väliajoin

LastIndexOfAny Ylikuormitetut versiot ovat samanlaisia ​​kuin IndexOfAny()-menetelmä

Palauttaa kutsun merkkijonosta löytyneen anyOf-taulukon minkä tahansa merkin viimeisen esiintymän indeksin

Merkkien jakaminen ja yhdistäminen
Jakaa julkinen merkkijono Jaa (parametrien merkkien erotin)

Julkinen merkkijono Jaa (parametrien merkkien erotin, int count)

Menetelmä, joka palauttaa merkkijonotaulukon, jonka sisällä on tässä tapauksessa esiintyvät alimerkkijonot, jotka on erotettu toisistaan ​​määritetyn merkin tai merkkijonotaulukon elementeillä.

Split()-menetelmän ensimmäinen muoto jakaa kutsuvan merkkijonon komponenttiosiinsa. Tuloksena on taulukko, joka sisältää kutsuvasta merkkijonosta saadut osamerkkijonot. Näitä osamerkkijonoja rajaavat merkit välitetään erotintaulukossa. Jos erotintaulukko on tyhjä tai viittaa tyhjään merkkijonoon, alimerkkijonojen erottimena käytetään välilyöntiä. Ja toisessa muodossa tätä menetelmää palauttaa count-parametrin määrittämän alimerkkijonojen määrän.

julkinen merkkijono Split (parametrien merkkien erotin, StringSplitOptions-asetukset)

Julkinen merkkijono Split (merkkijonojen erotin, StringSplitOptions-asetukset)

Julkinen merkkijono Split (parametrien merkkien erotin, int count, StringSplitOptions-asetukset)

Julkinen merkkijono Split (merkkijonojen erotin, int count, StringSplitOptions-asetukset)

Split()-metodin kahdessa ensimmäisessä muodossa kutsumerkkijono jaetaan osiin ja palautetaan taulukko, joka sisältää kutsuvasta merkkijonosta saadut osamerkkijonot. Nämä osamerkkijonot erottavat merkit välitetään erotintaulukossa. Jos erotintaulukko on tyhjä, erottimena käytetään välilyöntiä. Ja tämän menetelmän kolmannessa ja neljännessä muodossa count-parametrin rajoittama rivien määrä palautetaan.

Mutta kaikissa muodoissa asetukset-parametri määrittää tietyn tavan käsitellä tyhjiä rivejä, jotka syntyvät, kun kaksi erotinta on vierekkäin. StringSplitOptions-luettelo määrittää vain kaksi arvoa: Ei mitään Ja PoistaEmptyEntries. Jos vaihtoehto on Ei mitään, tyhjät merkkijonot sisällytetään alkuperäisen merkkijonon lopulliseen jakotulokseen. Ja jos asetukset-parametriksi on asetettu RemoveEmptyEntries, tyhjät rivit jätetään pois alkuperäisen merkkijonon jakamisen lopputuloksesta.

Liittyä seuraan() julkinen staattinen merkkijono Liity (merkkijonojen erotin, merkkijonon arvo)

Julkinen staattinen merkkijono Join (merkkijonojen erotin, merkkijonon arvo, int aloitusindeksi, int count)

Muodostaa uuden merkkijonon yhdistämällä merkkijonojoukon sisällön.

Join()-menetelmän ensimmäinen muoto palauttaa merkkijonon, joka koostuu arvotaulukossa välitetyistä ketjutetuista alijonoista. Toinen muoto palauttaa myös merkkijonon, joka koostuu arvotaulukossa välitetyistä alijonoista, mutta ne on ketjutettu tietyssä määrässä arvotaulukon elementistä alkaen. Molemmissa muodoissa jokainen seuraava rivi on erotettu edellisestä rivistä erotinparametrilla määritetyllä erotinviivalla.

Täyttö- ja leikkauslinjat
Trimmata() julkinen merkkijono Trim()

Julkinen merkkijono Trim(params char trimChars)

Menetelmä, jonka avulla voit poistaa kaikki tietyn merkkijoukon esiintymät nykyisen rivin alusta ja lopusta.

Trim()-menetelmän ensimmäinen muoto poistaa kutsun merkkijonosta alku- ja loppuvälilyönnit. Ja tämän menetelmän toinen muoto poistaa kutsuvan merkkijonon alku- ja loppuesiintymät trimChars-taulukosta. Molemmat muodot palauttavat tuloksena olevan merkkijonon.

PadLeft() julkinen merkkijono PadLeft(int totalWidth)

Julkinen merkkijono PadLeft(int totalWidth, char paddingChar)

Voit täyttää merkkijonon vasemmalla puolella.

PadLeft()-menetelmän ensimmäinen muoto lisää välilyönnit kutsuvan merkkijonon vasemmalle puolelle niin, että sen kokonaispituus on yhtä suuri kuin totalWidth-parametrin arvo. Ja tämän menetelmän toisessa muodossa paddingChar-parametrilla merkityt merkit syötetään kutsuvan merkkijonon vasemmalle puolelle niin, että sen kokonaispituus on yhtä suuri kuin totalWidth-parametrin arvo. Molemmat muodot palauttavat tuloksena olevan merkkijonon. Jos totalWidth-parametrin arvo on pienempi kuin kutsuvan merkkijonon pituus, palautetaan kopio muuttumattomasta kutsumerkkijonosta.

PadRight() Sama kuin PadLeft()

Voit lisätä merkkijonon oikealla puolella.

Rivien lisääminen, poistaminen ja korvaaminen
Insert() julkinen merkkijono Lisää(int aloitusindeksi, merkkijonon arvo)

Käytetään rivin lisäämiseen toiseen, missä arvo tarkoittaa riviä, joka lisätään kutsuvalle riville startIndexissä. Metodi palauttaa tuloksena olevan merkkijonon.

Poista() julkinen merkkijono Poista(int startIndex)

Julkinen merkkijono Poista(int aloitusindeksi, int count)

Käytetään osan merkkijonosta poistamiseen. Poista()-menetelmän ensimmäisessä muodossa poistaminen alkaa startIndexin osoittamasta kohdasta ja jatkuu rivin loppuun asti. Ja tämän menetelmän toisessa muodossa count-parametrin määräämä merkkien määrä poistetaan merkkijonosta alkaen startIndex-indeksin osoittamasta paikasta.

Korvata() julkinen merkkijono Korvaa(char oldChar, char newChar)

Julkinen merkkijono Korvaa(merkkijono oldArvo, merkkijono uusiArvo)

Käytetään korvaamaan merkkijonon osa. Replace()-menetelmän ensimmäisessä muodossa kaikki merkin oldChar esiintymät kutsuvassa merkkijonossa korvataan merkillä newChar. Ja tämän menetelmän toisessa muodossa kaikki merkkijonon oldValue esiintymät kutsuvalla rivillä korvataan merkkijonolla newValue.

Vaihda kirjainkokoa
ToUpper() julkinen merkkijono ToUpper()

Muuttaa kaikki kutsuvan merkkijonon kirjaimet isoiksi.

Laskea() julkinen merkkijono ToLower()

Kaikki kutsuvan merkkijonon kirjaimet ovat pieniä.

Osamerkkijonon saaminen merkkijonosta
Alimerkkijono() julkinen merkkijono Alimerkkijono(int aloitusindeksi)

Julkinen merkkijono Alimerkkijono(int aloitusindeksi, int pituus)

Substring()-menetelmän ensimmäisessä muodossa alimerkkijono noudetaan alkaen startIndex-parametrin osoittamasta paikasta ja päättyen kutsuvan merkkijonon loppuun. Ja tämän menetelmän toisessa muodossa poimitaan alimerkkijono, joka koostuu pituusparametrin määrittämästä merkkien määrästä, alkaen startIndex-parametrin osoittamasta paikasta.

Seuraava esimerkkiohjelma käyttää useita yllä olevista menetelmistä:

Järjestelmän käyttö; käyttäen System.Collections.Generic; käyttäen System.Linq; käyttäen System.Text; nimitila ConsoleApplication1 ( luokka Ohjelma ( staattinen void Main(merkkijono args) ( // Vertaa kahta ensimmäistä riviä string s1 = "tämä on merkkijono"; string s2 = "tämä on tekstiä ja tämä on merkkijono"); if (String. CompareOrdinal(s1, s2) != 0) Console.WriteLine("Merkkijonot s1 ja s2 eivät ole samat"); if (String.Compare(s1, 0, s2, 13, 10, true) == 0) Console.WriteLine ("Ne sisältävät kuitenkin samaa tekstiä"); // Merkkijonojen ketjuttaminen Console.WriteLine(String.Concat("\n" + "One, two ","kolme, neljä")); // Hae merkkijonosta / / Ensimmäinen osamerkkijonon esiintyminen if (s2. IndexOf("this") != -1) Console.WriteLine("Riviltä löytynyt sana \"this\", se "+ "on: (0) paikassa" , s2.IndexOf("this")); / / Alimerkkijonon viimeinen esiintyminen if (s2.LastIndexOf("this") != -1) Console.WriteLine("Sanan \"this\" viimeinen esiintymä on " + "(0) sijainnissa", s2.LastIndexOf("this" )); // Hae merkkijonosta char myCh = ("ы","x","t"); if (s2.IndexOfAny (myCh) != -1) Console.WriteLine("Yksi taulukon ch merkeistä "+ "löytyy nykyiseltä riviltä paikasta (0)", s2.IndexOfAny(myCh)); // Määritä, alkaako rivi annetulla osamerkkijonolla if (s2.StartsWith("tämä on teksti") == true) Console.WriteLine("Alimerkkijono löytyi!"); // Selvitä, sisältääkö merkkijono alimerkkijonon // käyttämällä esimerkkiä, jossa määritetään käyttäjän käyttöjärjestelmämerkkijono myOS = Environment.OSVersion.ToString(); if (myOS.Contains("NT 5.1")) Console.WriteLine("Sinun käyttöjärjestelmä Windows XP"); else if (myOS.Contains("NT 6.1")) Console.WriteLine("Käyttöjärjestelmäsi Windows-järjestelmä 7"); Console.ReadLine(); ) ) )

Vähän merkkijonojen vertailusta C#:ssa

Todennäköisesti yleisin kaikista merkkijonotoimista on merkkijonon vertaaminen toiseen. Ennen kuin tarkastelemme merkkijonojen vertailumenetelmiä, on syytä korostaa seuraavaa: Merkkijonojen vertailut voidaan tehdä .NET Frameworkissa kahdella päätavalla:

    Ensinnäkin vertailu voi heijastaa tietyn kulttuuriympäristön tapoja ja normeja, jotka ovat usein kulttuuriympäristöjä, jotka tulevat esiin ohjelmaa toteutettaessa. Tämä on normaalia toimintaa joillekin, mutta ei kaikille vertailumenetelmille.

    Ja toiseksi, vertailu voidaan tehdä kulttuurisista asetuksista riippumatta vain merkkijonon muodostavien merkkien järjestysarvoilla. Yleisesti ottaen merkkijonojen ei-kulttuuriset vertailut käyttävät leksikografista järjestystä (ja kielellisiä piirteitä) määrittämään, onko yksi merkkijono suurempi, pienempi tai yhtä suuri kuin toinen merkkijono. Järjestysvertailussa merkkijonot järjestetään yksinkertaisesti kunkin merkin muokkaamattoman arvon perusteella.

Koska kulttuuristen merkkijonojen vertailut ja järjestysvertailut eroavat toisistaan, ja kunkin vertailun seuraukset, suosittelemme, että noudatat Microsoftin tällä hetkellä tarjoamia parhaita käytäntöjä. Loppujen lopuksi väärän menetelmän valinta merkkijonojen vertailuun voi johtaa ohjelman virheelliseen toimintaan, kun sitä käytetään eri ympäristössä kuin siinä, jossa se on kehitetty.

Merkkijonojen vertailutavan valinta on erittäin tärkeä päätös. Yleissääntönä ja poikkeuksetta merkkijonojen vertailu kannattaa valita kulttuurisesti herkällä tavalla, jos tämä tehdään tulosten näyttämiseksi käyttäjälle (esimerkiksi merkkijonojen sarjan näyttämiseksi leksikografisessa järjestyksessä). Mutta jos merkkijonot sisältävät kiinteitä tietoja, joita ei ole tarkoitus muokata kulttuurierojen huomioon ottamiseksi, kuten tiedoston nimi, avainsana, verkkosivuston osoite tai turvallisuuteen liittyvä arvo, sinun tulee valita järjestysmerkkijonojen vertailu. Tietenkin tietyn kehitettävän sovelluksen ominaisuudet sanelevat sopivan menetelmän valinnan merkkijonojen vertailua varten.

String-luokka tarjoaa eniten erilaisia ​​menetelmiä vertaamalla yllä olevassa taulukossa lueteltuja merkkijonoja. Yleisin niistä on Compare()-menetelmä. Sen avulla voidaan verrata kahta merkkijonoa kokonaan tai osittain, isot ja pienet kirjainkoolla, tyyppiparametrin määrittelemällä tavalla StringComparison, sekä tyyppiparametrin tarjoamat kulttuuritiedot Kulttuuritiedot.

Ne Compare()-menetelmän ylikuormitukset, jotka eivät sisällä StringComparison-tyyppistä parametria, suorittavat kirjainkoolla ja kulttuurilla herkän merkkijonojen vertailun. Ja niissä ylikuormitetuissa muunnelmissa, jotka eivät sisällä CultureInfo-tyyppiparametria, tiedot kulttuuriympäristöstä määräytyvät nykyisen ajonaikaisen ympäristön mukaan.

StringComparison-tyyppi on luettelo, joka määrittelee alla olevassa taulukossa näkyvät arvot. Näiden arvojen avulla voit luoda merkkijonovertailuja, jotka sopivat sovelluksesi tarpeisiin. Siksi StringComparison-tyypin parametrin lisääminen laajentaa Compare()-menetelmän ja muiden vertailumenetelmien, kuten Equals() ominaisuuksia. Tämä mahdollistaa myös yksiselitteisen ilmaisemisen, kuinka merkkijonoja on tarkoitus verrata.

Koska kulttuurisesti herkkien merkkijonovertailujen ja järjestysvertailujen välillä on eroja, on tärkeää olla tässä suhteessa mahdollisimman tarkka.

StringComparison-luettelossa määritellyt arvot
Merkitys Kuvaus
CurrentCulture Merkkijonovertailut tehdään nykyisen kulttuuriympäristön asetuksilla
CurrentCultureIgnoreCase Merkkijonovertailut tehdään nykyisillä kulttuuriasetuksilla, mutta kirjainkoolla ei ole merkitystä
Muuttumaton kulttuuri Merkkijonovertailut tehdään käyttämällä muuttumattomia, ts. yleismaailmallista tietoa kulttuuriympäristöstä
InvariantCultureIgnoreCase Merkkijonovertailut tehdään käyttämällä muuttumattomia, ts. yleismaailmallinen kulttuuridata ja kirjainkoolla ei ole merkitystä
Tavallinen Merkkijonojen vertailut tehdään käyttämällä merkkijonon merkkien järjestysarvoja. Tässä tapauksessa leksikografinen järjestys voi häiriintyä ja symboleja Tietyssä kulttuuriympäristössä hyväksytyt asiat jätetään huomiotta
OrdinalIgnoreCase Merkkijonojen vertailut tehdään käyttämällä merkkijonon merkkien järjestysarvoja, mutta kirjainkoolla ei ole merkitystä

Joka tapauksessa Compare()-menetelmä palauttaa negatiivisen arvon, jos ensimmäinen verrattu merkkijono on pienempi kuin toinen; positiivinen, jos ensimmäinen verrattu merkkijono on suurempi kuin toinen; ja lopuksi nolla, jos molemmat verrattavat merkkijonot ovat yhtä suuret. Vaikka Compare()-menetelmä palauttaa nollan, jos vertailtavat merkkijonot ovat yhtä suuret, on yleensä parempi käyttää Equals()-metodia tai ==-operaattoria määrittääksesi, ovatko merkkijonot yhtä suuret.

Tosiasia on, että Compare()-menetelmä määrittää verrattujen merkkijonojen tasa-arvon niiden lajittelujärjestyksen perusteella. Siten jos merkkijonojen välillä tehdään kulttuurinen vertailu, molemmat merkkijonot voivat päätyä samoiksi lajittelujärjestyksensä, mutta eivät ole samanarvoisia. Oletusarvoisesti merkkijonojen tasa-arvo määritetään Equals()-menetelmällä, joka perustuu merkkien järjestysarvoihin ja ottamatta huomioon kulttuuriympäristöä. Siksi oletusarvoisesti molempia merkkijonoja verrataan tässä menetelmässä absoluuttisen, merkkikohtaisen yhtäläisyyden saamiseksi, samalla tavalla kuin ==-operaattorissa.

Huolimatta Compare()-menetelmän monipuolisuudesta, yksinkertaisissa merkkijonojen järjestysvertailuissa on helpompi käyttää CompareOrdinal()-menetelmää. Lopuksi muista, että CompareTo()-menetelmä suorittaa vain kulttuurisesti herkkiä merkkijonovertailuja.

Seuraava ohjelma esittelee Compare(), Equals(), CompareOrdinal()-menetelmien sekä ==- ja !=-operaattoreiden käytön merkkijonojen vertailuun. Huomaa, että kaksi ensimmäistä vertailuesimerkkiä osoittavat selvästi erot kulttuurisesti herkkien merkkijonovertailujen ja järjestysvertailujen välillä englanninkielisessä ympäristössä:

Järjestelmän käyttö; class Esimerkki ( staattinen void Main() ( string str1 = "alpha"; string str2 = "Alpha"; string str3 = "Beta"; string str4 = "alpha"; string str5 = "alfa, beta"; int tulos; / / Osoita ensin erot kulttuurisensitiivisen merkkijonovertailun // ja järjestysvertailutuloksen välillä = String.Compare(str1, str2, StringComparison.CurrentCulture); Console.Write("Kulttuurisensitiivisten merkkijonojen vertailu: "); if (tulos 0 ) Console.WriteLine(str1 + " suurempi kuin " + str2); muuten Console.WriteLine(str1 + " yhtä suuri kuin " + str2); tulos = String.Compare(str1, str2, StringComparison.Ordinal); Console.Write(" Ordinaaliset vertailurivit: "); if (tulos 0) Console.WriteLine(str1 + " suurempi kuin " + str2); else Console.WriteLine(str1 + " yhtä kuin " + str4); // Käytä CompareOrdinal()-menetelmän tulosta = String.CompareOrdinal( str1, str2); Console.Write("Merkkijonojen vertaaminen CompareOrdinal()-metodilla:\n"); if (tulos 0) Console.WriteLine(str1 + " suurempi kuin " + str2); else Console .WriteLine(str1 + " yhtä kuin " + str4); Console.WriteLine(); // Merkkijonojen tasa-arvon määrittäminen ==-operaattorilla // Tämä on merkkijonojen järjestysvertailu if (str1 == str4) Console.WriteLine(str1 + " == " + str4); // Määritä riviepäyhtälö käyttämällä !=-operaattoria if(str1 != str3) Console.WriteLine(str1 + " != " + str3); if(str1 != str2) Console.WriteLine(str1 + " != " + str2); Console.WriteLine(); // Suorita merkkijonojen järjestysvertailu, jossa kirjainkoolla ei ole merkitystä // käyttämällä Equals()-metodia if(String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) Console.WriteLine("Merkkijonojen vertailu Equals()-menetelmällä " + "OrdinalIgnoreCase-parametri: \n" + str1 + " on yhtä kuin " + str2); Console.WriteLine(); // Merkkijonojen osien vertailu if(String.Compare(str2, 0, str5, 0, 3, StringComparison.CurrentCulture) > 0) ( Console.WriteLine("Vertaa merkkijonoja nykyisen kulttuuriympäristön mukaan:" + "\n3 merkkijonon " + str2 + " ensimmäiset merkit enemmän kuin rivin 3 ensimmäistä merkkiä " + str5); ) ) )

Tämän ohjelman suorittaminen tuottaa seuraavan tulosteen:

SISÄÄN moderni standardi C++ määrittelee luokan funktioilla ja ominaisuuksilla (muuttujilla) merkkijonojen kanssa työskentelyn järjestämiseksi (klassisessa C-kielessä ei ole merkkijonoja sellaisenaan, on vain merkkijonoja):

#sisältää

#sisältää

#sisältää

Jotta voit työskennellä merkkijonojen kanssa, sinun on myös yhdistettävä vakionimiavaruus:

Nimiavaruuden käyttäminen std;

Muussa tapauksessa sinun on määritettävä std::string-luokkakuvaus kaikkialla merkkijonon sijaan.

Alla on esimerkki ohjelmasta, joka toimii merkkijonon kanssa (ei toimi vanhemmissa C-yhteensopivissa kääntäjissä!):

#sisältää #sisältää #sisältää käyttäen nimiavaruutta std; int main() ( string s = "Testaa"; s.insert(1,"!"); cout<< s.c_str() << endl; string *s2 = new string("Hello"); s2->erase(s2->end()); cout<< s2->c_str(); cin.get(); paluu 0; )

Merkkijonoluokan pääominaisuudet:

  • alustus merkkijonolla (sisäänrakennettu merkkijonotyyppi) tai muulla objektilla, jonka tyyppi on string . Sisäänrakennetulla tyypillä ei ole toista ominaisuutta;
  • kopioida riviä toiselle. Sisäänrakennetulle tyypille on käytettävä strcpy()-funktiota;
  • pääsy merkkijonon yksittäisiin merkkeihin lukemista ja kirjoittamista varten. Sisäänrakennetussa taulukossa tämä tehdään käyttämällä indeksitoimintoa tai epäsuoraa osoitetta osoittimen avulla;
  • vertaamalla kahta merkkijonoa tasa-arvoon. Sisäänrakennetussa tyypissä käytetään strcmp()-perheen toimintoja;
  • kahden merkkijonon ketjuttaminen (ketjuttaminen), jolloin tuloksena saadaan joko kolmantena merkkijonona tai jonkin alkuperäisen merkkijonon sijasta. Sisäänrakennetussa tyypissä käytetään strcat()-funktiota, mutta saadaksesi tuloksen uudelle riville, sinun on käytettävä strcpy()- ja strcat()-funktioita peräkkäin ja huolehdittava myös muistin varaamisesta;
  • sisäänrakennettu keino merkkijonon pituuden määrittämiseen (luokan jäsenfunktiot size() ja l ength()). Ainoa tapa selvittää sisäänrakennetun merkkijonon pituus on laskea se strlen()-funktiolla;
  • kyky selvittää, onko merkkijono tyhjä.

Katsotaanpa näitä perusominaisuuksia tarkemmin.

Merkkijonojen alustus kuvattaessa ja langan pituus(ei sisällä päättyvää nollapäätettä):

String st("Oma merkkijono\n"); cout<< "Длина " << st << ": " << st.size() << " символов, включая символ новой строки\n";

Merkkijono voi olla myös tyhjä:

merkkijono st2;

Sen tarkistamiseksi onko rivi tyhjä, voit verrata sen pituutta 0:aan:

Jos (! st.size()) // tyhjä

tai käytä tyhjä()-metodia, joka palauttaa tosi tyhjälle merkkijonolle ja false ei-tyhjälle:

Jos (st.empty()) // tyhjä

Kolmas merkkijonon luomismuoto alustaa merkkijonotyypin objektin toisella samantyyppisellä objektilla:

merkkijono st3(st);

Merkkijono st3 alustetaan merkkijonolla st . Kuinka voimme varmistaa nämä linjat täsmäävät? Käytetään vertailuoperaattoria (==):

Jos (st == st3) // alustus toimi

Miten kopioi rivi toiselle? Normaalia määritysoperaattoria käyttämällä:

St2 = st3; // kopioi st3 st2:een

varten merkkijonojen ketjutus käytetään summausoperaattoria (+) tai summaus- ja osoitusoperaattoria (+=). Annetaan kaksi riviä:

Merkkijono s1("hei, "); merkkijono s2("maailma\n");

Voimme saada kolmannen merkkijonon, joka koostuu kahden ensimmäisen ketjutuksesta, tällä tavalla:

Merkkijono s3 = s1 + s2;

Jos haluamme lisätä s2:n s1:n loppuun, meidän tulee kirjoittaa:

S1 += s2;

Lisäystoiminto voi ketjuttaa luokkaobjekteja merkkijono ei vain keskenään, vaan myös sisäänrakennetuilla merkkijonoilla. Voit kirjoittaa yllä olevan esimerkin uudelleen siten, että erikoismerkit ja välimerkit esitetään sisäänrakennetulla char * -tyypillä ja merkitseviä sanoja edustavat luokkamerkkijonon objektit:

Const char *pc = ", "; merkkijono s1("hei"); merkkijono s2("maailma"); merkkijono s3 = s1 + pc + s2 + "\n"; cout<< endl << s3;

Tällaiset lausekkeet toimivat, koska kääntäjä "osaa" automaattisesti muuntaa sisäänrakennetun tyypin objektit merkkijonoluokan objekteiksi. On myös mahdollista yksinkertaisesti määrittää sisäänrakennettu merkkijono merkkijonoobjektiin:

merkkijono s1; const char *pc = "merkkijono"; s1 = PC; // Aivan

Käänteinen muunnos tässä tapauksessa ei toimi. Seuraavan sisäänrakennetun merkkijonon alustuksen yrittäminen aiheuttaa käännösvirheen:

Char *str = s1; // käännösvirhe

Suorittaaksesi tämän muunnoksen, sinun on kutsuttava nimenomaisesti jäsenfunktio nimeltä c_str() ("C-merkkijono"):

Const char *str = s1.c_str();

Funktio c_str() palauttaa osoittimen merkkijonoon, joka sisältää merkkijonoobjektin merkkijonon sellaisena kuin se esiintyy sisäänrakennetussa merkkijonotyypissä. Const-avainsana estää tässä nykyaikaisissa visuaalisissa ympäristöissä "vaarallisen" mahdollisuuden muokata kohteen sisältöä suoraan osoittimen avulla.

TO yksittäisiä hahmoja objektia, jonka tyyppi on string, kuten sisäänrakennettua tyyppiä, voidaan käyttää indeksitoiminnolla. Esimerkiksi tässä on koodinpätkä, joka korvaa kaikki pisteet alaviivalla:

String str("www.disney.com"); int koko = str.size(); for (int i = 0; i< size; i++) if (str[i] == ".") str[ i ] = "_"; cout << str;

Korvaa(str.begin(), str.end(), ".", "_");

Totta, tässä ei käytetä merkkijonoluokan korvausmenetelmää, vaan samannimistä algoritmia:

#sisältää

Koska merkkijonoobjekti käyttäytyy kuin säiliö, siihen voidaan soveltaa muita algoritmeja. Näin voit ratkaista ongelmia, joita merkkijonoluokan funktiot eivät suoraan ratkaise.

Alla on lyhyt kuvaus merkkijonoluokan pääoperaattoreista ja toiminnoista, taulukon linkit johtavat venäjänkielisiin kuvauksiin Internetissä. Täydellisempi luettelo merkkijonoluokan ominaisuuksista löytyy esimerkiksi Wikipediasta tai verkkosivuilta cplusplus.com.

Merkkien määrittäminen merkkijonossa

operaattori =

määrittää arvot merkkijonolle

antaa

määrittää merkkijonoon merkkejä

Pääsy yksittäisiin hahmoihin

klo

hakea määritetyn merkin ja tarkistaa, onko indeksi rajojen ulkopuolella

operaattori

määritetyn merkin saaminen

edessä

saada ensimmäinen hahmo

takaisin

saada viimeinen hahmo

tiedot

palauttaa osoittimen merkkijonon ensimmäiseen merkkiin

c_str

palaa muokkaamaton C-merkkijono, joka sisältää merkkijonon merkit

Linjakapasiteetin tarkistus

tyhjä

tarkistaa, onko merkkijono tyhjä

koko
pituus

palauttaa merkkijonon merkkien määrän

max_size

palauttaa enimmäismäärän merkkejä

varata

varaa säilytystilaa

Merkkijonotoiminnot

asia selvä

tyhjentää merkkijonon sisällön

lisää

merkkien lisääminen

pyyhkiä

merkkien poistaminen

työnnä takaisin

merkin lisääminen merkkijonon loppuun

pop_back

poistaa viimeisen merkin

liittää

operaattori+=

lisää merkkejä merkkijonon loppuun

vertailla

vertaa kahta merkkijonoa

korvata

korvaa jokaisen määritetyn merkin esiintymän

substr

palauttaa alimerkkijonon

kopio

kopioi merkkejä

muuttaa kokoa

muuttaa tallennettujen merkkien määrää