Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Tekstien vertailu (C)

Sivun loppuun

Thomas Taussi [10.02.2008 13:25:39]

#

Aloitinpa tänään aamulla tutustumisen C-ohjelmointiin. Luin jotenkin täältä löytyvistä oppaista satunnaisia pääkohtia. Ajattelin tehdä harjoitustyönä jonkinlaisen minikonsolin, joka reagoisi tietynlaisiin syötemerkkijonoihin.
Tässä on koodini, joka ei toimi tuon säännöllisiin lausekkeisiin viittaavan rakenteen kohdalta. Liekö sellaista tässä C-ohjelmoinnissa ollenkaan. Apua?

#include <stdio.h>

using namespace std;


int main(void) {
    /* tulostetaan tekstiä näytölle */
    printf("/komennot - komennot \n/exit - lopeta \n");

char komento[40]; /* tämän taulukon täytyy olla riittävän iso luettavalle merkkijonolle */
gets(komento);

if (komento == "/komennot") {
    printf("Komentolista on tyhjä");
}
else {
     printf("Komentoa ei tunneta tai se on kirjoitettu väärin\n");
}
getchar();

    return 0;
}

os [10.02.2008 13:31:59]

#

Olisikohan kannattanut lukea ihan mistä tahansa myös muiden esittämien kysymysten pääkohtia?

if (komento == "1") ei (tietenkään) toimi. Mistä sait päähäsi, että C-kieli tukee "säännöllisiä lausekkeita"?

Gaxx [10.02.2008 13:53:18]

#

Jos tämä nyt on olevinaan c:tä, using namespace std ei kuulu kuvaan - se on c++:aa. C:llä tuo vertailu toteutettaisiin näin:

if(strcmp(komento, "1") == 0)

Suosittelen lukemaan oppaan kokonaisuudessaan.

Jos taasen haluat opetella c++:aa, suosittelen tutustumaan tähän oppaaseen.

Ps. C-kieli kannattaa kääntää c-kääntäjällä :)

Thomas Taussi [10.02.2008 14:22:28]

#

Kiitos :) Yksinään tuo

if(strcmp(komento, "1") == 0)

näyttää toimivan jo aikaisemmin määriteltynä, mutta komennon määrittelevä

gets(komento);

asettuu ohjelman mukaan ongelmaksi ja homma tyssää.

Metabolix [10.02.2008 14:56:02]

#

Mitä tarkoitat? Hyvinhän tuo kääntyy, kun tekee mainitut muutokset (eli muuttaa vertailun ja poistaa using-rivin). GCC kuitenkin antaa tällaisen varoituksen (joka siis ei tarkoita, etteikö ohjelma silti toimisi):
warning: the 'gets' function is dangerous and should not be used.
Varoitus on aivan oikeassa. Syötettä kannattaa ehdottomasti lukea jollakin muulla välineellä, koska gets ei osaa lopettaa taulukon loppuessa vaan lukee niin paljon, kuin käyttäjä kirjoittaa, joten yli 39 merkin syötteen antaminen aiheuttaa ohjelmassasi tuhoa ja hävitystä. Hyvä vaihtoehto on fgets-funktio, joka huomioi myös maksimikoon:

fgets(teksti, taulun_koko, tiedosto);
fgets(komento, sizeof(komento), stdin);

Muutin otsikon kysymystä vastaavaksi. Säännöllisiin lauseisiin voit tutustua niistä kertovassa oppaassa. Ne eivät liity tähän asiaan eivätkä muutenkaan C-kieleen (vaikka varmasti on lisäkirjastoja, jotka mahdollistavat niiden käytön).

Ilmeisesti sinulla on ennestään kokemusta PHP:stä. C:n kanssa kannattaa siis pitää mielessä yski olennainen asia: PHP esittää ymmärtävänsä, mitä haluat. C ei. Täten esimerkiksi C:n yhtäsuuruusvertailu on vielä täsmällisempi kuin PHP:n ===-vertailu.

Thomas Taussi [10.02.2008 16:47:55]

#

Heh.. olipa osuvasti sanottu tuon PHP:n kohdalta :D
Arvasit oikein: PHP:stä ja Quick- sekä Coolbasicista on kokemusta, ja olen joutunut huomaamaan, että C todellakin käy asiat läpi perinpohjaisemmin, mikä myös jättää enemmän vaihtoehtoja.

En kuitenkaan jostain syystä saanut tuota toimimaan.. kummallakaan kääntäjällä vaikka valintanakin oli C eikä C++.

Koodi oli vielä tällainen

#include <stdio.h>



int main(void) {
    /* tulostetaan tekstiä näytölle */
    printf("/komennot - komennot \n/exit - lopeta \n");

int komento[40]; /* tämän taulukon täytyy olla riittävän iso luettavalle merkkijonolle */
fgets(komento, sizeof(komento), stdin);

if(strcmp(komento, "/komennot") == 0) {
    printf("komentolista on tyhjä");
}
else {
     printf("Komentoa ei tunneta tai se on kirjoitettu väärin\n");
}
getchar();

    return 0;
}

Legu [10.02.2008 16:57:56]

#

Mitävarten komennon alkioiden tyyppi on int? Muuta se char:ksi.

// int komento[40];
char komento[40];

Metabolix [10.02.2008 17:12:07]

#

Alkuperäisessä koodissa tuo oli oikein, olisit verrannut siihen. Ja edelleenkin C-kääntäjä kääntää tuon virheestä huolimatta oikein, C++ sen sijaan ei salli tuollaista. Myös niitä kääntäjän ilmoituksia kannattaa lukea, siinä lukee luultavasti jotain aiheesta "incompatible pointer type", mikä jo kertoo, että jokin parametreista on väärää tyyppiä.

Unohtui mainita, että fgets lukee myös rivinvaihtomerkin puskuriin, joten se täytyy erikseen käydä poistamassa:

fgets(komento, sizeof(komento), stdin);
pituus = strlen(komento); /* Tietenkin "int pituus;" jossain kohti */
if (komento[pituus - 1] == '\n') {
  komento[pituus - 1] = 0; /* Lopetusmerkki siihen */
} else {
  printf("Rivi ei mahtunut puskuriin tai syöte loppui kesken!\n");
}

Toinen hyvä lukutapa on scanf, kannattaa tutustua siihenkin.

Thomas Taussi [10.02.2008 17:39:55]

#

Se alkaa valittamaan nyt tuosta rivistä

pituus = strlen(komento); /* Tietenkin "int pituus;" jossain kohti */

Legu [10.02.2008 17:58:44]

#

Mikähän siinä on, kun pitää vaan sanoa, että "Se alkaa valittamaan nyt tuosta rivistä"? Voi sen virheilmoituksenkin tänne laittaa. No kuitenkin nyt oletan, että joko sinulla ei ole missään tuota "int pituus;" tai sitten et ole sisällyttänyt string.h:ta jossa strlen-funktio määritellään.

Selvyyden vuoksi nyt koodikin vielä:

#include <stdio.h>
#include <string.h>

int main(void)
{
	char komento[40];
	int pituus;

    printf("/komennot - komennot \n/exit - lopeta \n");
	fgets(komento, sizeof(komento), stdin);
	pituus = strlen(komento);
	if (komento[pituus - 1] == '\n') {
		komento[pituus - 1] = 0;
	} else {
		printf("Rivi ei mahtunut puskuriin tai syöte loppui kesken!\n");
		// Kenties jotain jatkotoimenpiteitä...?
	}

	if(strcmp(komento, "/komennot") == 0) {
		printf("komentolista on tyhjä\n");
	}
	else {
		 printf("Komentoa ei tunneta tai se on kirjoitettu väärin\n");
	}

    return 0;
}

Thomas Taussi [10.02.2008 18:17:03]

#

Kiitoksia! Pienellä värkkäilyllä sain tuon pelaamaan jo jotenkin.. Nyt painun oppimaan kokeilemalla :) Seuraavaan ongelmaan ->

Thomas Taussi [10.02.2008 19:54:44]

#

Törmäsin kahteen kysymykseen:
1. Mitenköhän ääkköset saisi toimimaan? (Mieleen tuli joku korvausfunktio ja ascii-koodit)
2. Onko C:ssä mahdollista pilkkoa merkkijono sanoihin?

kayttaja-2499 [10.02.2008 20:19:18]

#

2. http://www.cppreference.com/stdstring/strtok.html

Metabolix [10.02.2008 20:19:54]

#

1. Katso keskustelut 13904 ja 1649. Ongelmahan on juuri siinä, että ääkköset eivät kuulu 7-bittiseen ASCII-merkistöön vaan vain sen eräisiin 8-bittisiin ja suurempiin laajennoksiin.

2. Kyllä, strtok-funktiolla, joka kylläkin muuttaa samalla alkuperäistä tekstiä.

#include <string.h>
#include <stdio.h>
int main(void)
{
	char s[] = "a # b # c";
	char d[] = "#";
	char *ptr = NULL;
	printf("Alku: %p: '%s'\n", s, s);
	for (ptr = strtok(s, d); ptr != NULL; ptr = strtok(NULL, d)) {
		printf("Pala: %p: '%s'\n", ptr, ptr);
	}
	/* Huomaa tulostuksessa muutos: 'a ' */
	printf("Alku: %p: '%s'\n", s, s);
	return 0;
}

Osoittimet joudut itse keräämään taulukkoon, jonka tietenkin pitää myös olla riittävän iso niille.

Thomas Taussi [15.02.2008 07:25:21]

#

Kiitos.. toimii ainakin tuo paloittelu. Mitenköhän tuon char s[] arvon saisi muutettua vaikka sen inputin komento arvoksi?

Gaxx [15.02.2008 20:51:55]

#

Vaikka lukemalla siihen se syöte(input). Äläkä väitä, ettet muka osaisi — on aika ajatella itse.

Thomas Taussi [16.02.2008 11:48:08]

#

Taisin jo kerran koittaa sillä tavalla, mikä sai minut kysymään apua, mutta voinhan koittaa uudestaan yms.. :)

Gaxx [16.02.2008 12:54:26]

#

Olisi hyvä laittaa vaikka se koodi, millä yritit tehdä sitä. Valmista riviä en anna, mutta koitan nyt antaa vinkkiä...

Jos välttämättä haluat lukea syötteen komento-taulukkoon ja sitten kopioida sen s-taulukkoon, onnistuu se seuraavalla funktiolla

strcpy(kohde, lahde);

Veikkaan kuitenkin sinua hämänneen merkintä s[]. Seuraavat kaksi koodinpätkää tekevät saman asian.

char s[] = "a # b # c"; /* Varaa riittävästi muistia ja alustaa merkkijonolla */
printf(s);
char s[10]; /* Varataan char-taulukko(kymmenelle alkiolle) */
strcpy(s, "a # b # c"); /* Kopioidaan merkkijono taulukkoon */
printf(s);

Legu [16.02.2008 17:49:52]

#

Ja vielä varoitus edelliseen viestiin liittyen:

char s[] = "% on prosenttimerkki!";
printf(s);

Kyseinen koodi tulosti "1001113020n prosenttimerkki!".

char s[] = "% on prosenttimerkki!";
printf("%s", s);

Tämä taas tulosti "% on prosenttimerkki!", kuten oli tarkoitus.

Kannattaa siis huomata noiden ero. Jos haluaa tulostaa jonkun merkkijonon sellaisenaan (kuten edellisen viestin esimerkissä), niin silloin on syytä käyttää muotoa printf("%s", s).

Kray [18.02.2008 15:44:39]

#

Ja (en muista oliko näin, en jaksa nyt katsoa :D) siihen käynee myös puts(s);

Blaze [18.02.2008 18:12:33]

#

puts laittaa rivinvaihdon tulostettavan merkkijonon perään. Käy siis usein, muttei aina.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta