string
en iostream
.
© Harry Broeders.
Deze opdracht is bedoeld om je C kennis op te frissen en behandelt daarnaast
enkele kleine verbeteringen van C++ ten opzichte van C. In de propedeuse
heb je leren werken met character array's voor het opslaan van strings. In
de standaard C++ library is het type string
gedefinieerd dat
veel eenvoudig te gebruiken is dan character array's. In C gebruik je de
stdio
library voor input en output bewerkingen. In de C++ standaard
is de iostream
library gedefinieerd die eenvoudiger te gebruiken
is dan de stdio
library. Deze nieuwe library is bovendien
uitbreidbaar (zoals we in opdracht 2 zullen zien).
Lees voordat je met deze opgave gaat beginnen eerst hoofdstuk 1 van het dictaat door.
string
type.
We zullen eerst de problemen met strings in C op een rijtje zetten en daarna
het nieuwe string
type uit C++ bespreken.
De programmeertaal C heeft geen "echt" string type. Als je in C een string
(rij karakters) wilt opslaan dan doe je dat in een character array. In C
geldt de afspraak dat elke string wordt afgesloten door een zogenaamd
nul-karakter '\0'
. Voorbeeld:
char naam[] = "Harry"; // deze array bevat 6 karakters!
Een string in C heeft de volgende problemen:
strxxx
functies gebruikt worden).
#include <string.h>
#include <iostream.h>
int main() {
char naam[10];
naam = "Harry"; // Error! Zie opmerking 1.
strcpy(naam, "Harry"); // OK
cout<<naam<<endl;
strcpy(naam, "Willem-Alexander"); // Error! Zie opmerking 2.
strcpy(naam, "Alex"); // OK
cout<<naam<<endl;
if (naam == "Alex") { // Error! Zie opmerking 3.
// ...
}
if (strcmp(naam, "Alex") == 0) { // OK
// ...
}
}
Opmerkingen:
naam
) kun je namelijk niets toekennen. Als je de
array wilt vullen dan moet je dat met de functie strcpy
(gedefinieerd in <string.h>
) doen.
naam == "Alex"
levert echter altijd false
op! Als
je twee array variabelen met elkaar vergelijkt dan worden hun adressen
met elkaar vergeleken. Als je de inhoud van de array's met elkaar
wilt vergelijken moet je de functie strcmp
(gedefinieerd in
<string.h>
) gebruiken.
string
.
De programmeertaal C++ heeft wel een "echt" string type. Dit type is gedefinieerd in de standaard library. Later zul je leren dat string geen "ingebouwd" type is maar een "zelfgemaakt type" een zogenaamde class. Voor het gebruik maakt dat echter niet uit. Voorbeeld:
#include <string>
using namespace std;
string naam("Harry"); // deze string bevat 5 karakters.
Een string
in C++ heeft de volgende voordelen ten opzichte van
een string uit C:
string
is dynamisch (de lengte kan tijdens het uitvoeren
van het programma, indien nodig, worden aangepast, er is dus ook geen maximale
lengte!).
string
bevat veel extra functionaliteit.
#include <string> // Zie opmerking 1.
#include <iostream>
using namespace std;
int main() {
string naam;
naam = "Harry"; // Zie opmerking 2.
cout<<naam<<endl;
naam = "Willem-Alexander"; // Zie opmerking 3.
cout<<naam<<endl;
if (naam == "Willem-Alexander") { // Zie opmerking 4.
cout<<"Hoi Alex!"<<endl;
}
return 0;
}
Opmerkingen:
<string>
is dus heel wat anders dan
de C include file <string.h>
. Als je in een C++ programma
toch de oude C strings wilt gebruiken (om oude C code te hergebruiken) dan
kun je de oude strxxx
functies includen met de include file
<cstring>
.
string
met behulp van de operator =
.
string
is dynamisch en "groeit" als dat nodig is!
string
gewoon vergelijken met
behulp van de operator ==
.
Nu komt de verrassing: een variabele van het type (eigenlijk de class)
string
is geen gewone variabele maar een object! Wat
dat precies betekent wordt in hoofdstuk 3 van het
dictaat uitgebreid behandeld. Op dit moment
zullen we alleen bekijken wat dit betekent voor het gebruik van objecten
(variabelen) van de class (het type) string
.
Objecten zijn vergelijkbaar met gewone variabelen: ze hebben een naam en je kunt er "iets" in opslaan. Maar met objecten kun je iets wat met gewone variabelen niet kan. Je kunt objecten boodschappen (messages) sturen.
Je kunt een message naar een object sturen om het object een vraag
te stellen. Als je wilt weten hoeveel karakters een object van de class
string
bevat dan kun je dat object de message size
sturen. Het antwoord op deze message is dan een integer die het aantal karakters
weergeeft.
#include <string>
#include <iostream>
using namespace std;
int main() {
string naam("Willem-Alexander");
cout<<"De naam "<<naam<<" bevat "<<naam.size()<<" karakters."<<endl;
cin.get();
return 0;
}
Uitvoer:
De naam Willem-Alexander bevat 16 karakters.
De syntax voor het versturen van een message is:
naam-van-object.naam-van-message(parameters)
Dus eerst de naam van het object waar naar toe de message verstuurd moet
worden (dat object wordt de receiver van de message genoemd) dan een
punt gevolgd door de naam van de message en tot slot een haakje openen en
een haakje sluiten met daartussen eventuele parameters. De message
size
heeft geen parameters. Het versturen van een message lijkt
een beetje op het aanroepen van een functie. In C++ wordt het versturen van
een message meestal het aanroepen van een memberfunctie genoemd. Toch
is er een duidelijk verschil tussen een memberfunctie (message) en een functie:
een memberfunctie heeft een receiver en een gewone functie niet!
Je kunt ook een message naar een object sturen om het object iets te laten
doen. Na afloop van de memberfuntie is het object dan veranderd. Als
je bijvoorbeeld iets aan een object van de class string
wilt
toevoegen dan kun je dat object de message append
sturen. De
string
die moet worden toegevoegd moet je als argument meesturen.
#include <string>
#include <iostream>
using namespace std;
int main() {
string naam("Willem-Alexander");
naam.append(" en Maxima");
cout<<naam<<endl;
cin.get();
return 0;
}
Uitvoer:
Willem-Alexander en Maxima
string
.
Voor het complete overzicht verwijs ik je naar de helpfile van de class
string
. Als je in Borland C++ Builder het woordje
string
intypt en op F1 drukt moet je kiezen voor
basic_string
voor het complete overzicht. De typenaam
string
blijkt een andere naam te zijn voor het template type
basic_string
. Het begrip template wordt pas in hoofdstuk 4 van
het dictaat behandeld.
In deze paragraaf worden enkele voorbeelden gegeven.
>
<
>=
<=
==
!=
(spreekt
voor zich).
[]
.char
c(s[2]);
s
gelijk is aan Maxima
dan wordt het karakter
c
gelijk aan x
.
[]
.s[3]='a';
s
gelijk is aan Maxima
dan wordt het
s
gelijk aan Maxama
.
=
of met assign
.
Met assign
kun je ook een deel van de ene string
aan de andere string
toekennen.s1.assign(s2, 7,
4);
s1
wordt gelijk aan het deel van s2
dat begint op positie
7 en 4 karakters lang is. Als s2
gelijk is aan
Willem-Alexander
dan zal s1
gelijk worden aan
Alex
.
+
en +=
of met
append
. Met append
kun je ook een deel van de ene
string
achter de andere string
plakken.string
s1("Willem");
s1+="-Alexander";
string
s2("Maxima");
s2+=" en
";
s2.append(s1, 7,
4);
s2
wordt gelijk aan Maxima en Alex
insert
.s1.insert(4,
s2);
s2
op positie 4 in s1
in. Als s1
gelijk is aan Maxima
en s2
gelijk is aan
Willem-Alexander
dan wordt s1
gelijk aan
MaxiWillem-Alexanderma
.s1.insert(4, s2,
7,
4);
s1
gelijk is aan Maxima
en s2
gelijk is aan Willem-Alexander
dan wordt s1
gelijk
aan MaxiAlexma
.
erase
.s1.erase(3,
10);
s1
gelijk is aan Willem-Alexander
dan wordt
s1
gelijk aan Wilder
.
replace
.s1.replace(0,
4,
"Jongs");
s1
gelijk is aan Maxima
dan wordt
s1
gelijk aan Jongsma
.
substr
.s1=s2.substr(7);
s1
wordt gelijk aan het deel van s2
dat begint
op positie 7. Als s2
gelijk is aan Willem-Alexander
dan zal s1
gelijk worden aan Alexander
.s1=s2.substr(7,
4);
s1
wordt gelijk aan het deel van s2
dat begint
op positie 7 en 4 karakters lang is. Als s2
gelijk is aan
Willem-Alexander
dan zal s1
gelijk worden aan
Alex
.
find
.int
i(s1.find("Alex"));
s1
gelijk is aan Willem-Alexander
dan wordt
i
gelijk aan 7. Als de string Alex
niet in
s1
voorkomt krijgt i
de waarde
string::npos
(een in de class string
gedefinieerde
constante). Eigenlijk moeten we deze regel als volgt programmeren:string::size_type
i(s1.find("Alex"));
string
wordt namelijk het type
size_type
gedefinieerd dat gebruikt moet worden om indexwaarden
van een string
in op te slaan.string::size_type
i(s1.find("le"));
string::size_type
j(s1.find("le",
7));
s1
gelijk is aan Willem-Alexander
dan wordt
i
gelijk aan 3 en j
gelijk aan 8.find
de string van voor naar
achter. Je kunt de memberfunctie rfind
gebruiken om van achter
naar voor te zoeken.
find_first_of
.string::size_type
i(s1.find_first_of("aeiou"));
s1
die voorkomt in de als parameter
meegegeven string
. Als s1
gelijk is aan
Maxima
dan wordt i
gelijk aan 1. Als de string
s1
geen van de karakters a
, e
,
i
, o
of u
bevat krijgt i
de waarde string::npos
.find_last_of
,
find_first_not_of
en find_last_not_of
spreekt denk
ik voor zich.
const char*
met
c_str()
.string
.
Zoals je in paragraaf 1.7 van het dictaat
hebt gelezen gebruiken we in C++ de input/output library
iostream
in plaats van de C library stdio
. Het
zal je niet verbazen dat de variabelen cin
en cout
die je in paragraaf 1.7 hebt leren kennen geen variabelen maar objecten zijn.
Je kunt deze objecten (net als objecten van de class string
)
dus ook messages sturen. Elke class definieert echter zijn eigen messages.
Het object cout
is een object van de class ostream
en het object cin
is een object van de class
istream
. Later zal blijken dat dit niet helemaal klopt
maar dat maakt voor dit verhaal niets uit. Naar een object van de
class string
kun je bijvoorbeeld de message size
sturen om te vragen hoeveel karakters het object bevat. Naar
cout
kun je deze message echter niet sturen (dit object begrijpt
deze message niet). De aanroep cout.size()
geeft tijdens het
compileren de volgende foutmelding: 'size' is not a member of
'ostream'
. Welke messages je naar cout
en
cin
kunt sturen kun je opzoeken in de helpfile van de class
ostream
respectievelijk istream
. Bijvoorbeeld:
cout.fill('#');
cout.width(10);
int i(189);
cout<<i<<endl;
Uitvoer:
#######189
Tot slot van deze inleiding volgt nog een voorbeeld
waarin met objecten
van de class
string
wordt gewerkt.
Merk op dat in dit programma de vernieuwingen die in C++ zijn ingevoerd ten opzichte van C namelijk het gebruik van abstracte data typen en object georiënteerde technieken in dit programma nog niet toegepast zijn. In dit programma wordt C++ dus op een C manier gebruikt. Dit is voor kleine programma's geen probleem. Als een programma echter groter is of als het uitbreidbaar of onderhoudbaar moet zijn kunnen we beter gebruik maken van de object georiënteerde technieken die C++ biedt. Deze technieken zullen in de volgende practicumopdrachten aan de orde komen.
#include <iostream>
#include <string>
using namespace std;
int main () {
cout<<"Geef je email adres: ";
string mailAdres;
cin>>mailAdres;
string::size_type indexAapje(mailAdres.find("@"));
if (indexAapje!=string::npos) {
cout<<"Gebruiker: "
<<mailAdres.substr(0, indexAapje)
<<endl;
cout<<"Machine: "
<<mailAdres.substr(indexAapje+1)
<<endl;
}
else {
cout<<mailAdres<<" is geen geldig email adres!"<<endl;
}
cout<<"Druk op de return-toets."<<endl;
cin.get();
cin.get();
return 0;
}
Deze opdracht bestaat uit de deelopdrachten 1a tot en met 1c. Opdrachten 1b en 1c moet je laten aftekenen door de docent.
Opdracht 1a.
Compileer en test het bovenstaande programma
|
Opdracht 1c.
Soms is alleen 1 van de 4 onderdelen (protocol, machine, directory of file)
nodig. Het ontleden van de hele URL is dan overbodig. Schrijf één
of meer functies zodanig dat het volgende
testprogramma
De uitvoer moet dan zijn: |