Merkkijonojen käsittely
Tutustutaan seuraavaksi tarkemmin merkkijonoihin (String
) sekä kerrataan hieman niihin liittyviä perusominaisuuksia. Merkkijonomuuttuja määritellään kertomalla sen tyyppi (String) sekä nimi. Tätä seuraa muuttujan arvo, joka on hipsujen sisällä olevaa tekstiä. Alla luodaan merkkijonomuuttuja taikasana
, joka sisältää arvon "abrakadabra"
.
String taikasana = "abrakadabra";
Merkkijonomuuttujan antaminen tulostuskomennolle (tai oikeastaan mille tahansa metodille) parametrina onnistuu tutulla tavalla. Alla määritellään merkkijono, joka tulostetaan.
String taikasana = "abrakadabra";
System.out.println(taikasana);
abrakadabra
Merkkijonojen lukeminen ja tulostaminen
Kuten muistamme, merkkijonon lukeminen onnistuu tutun Scanner-apuvälineen tarjoamalla nextLine-metodilla. Alla oleva ohjelma lukee käyttäjän nimen ja tulostaa sen seuraavalla rivillä (esimerkissä käyttäjän syöttämä teksti on merkitty punaisella):
Scanner lukija = new Scanner(System.in);
System.out.print("Mikä on nimesi? ");
// Luetaan käyttäjältä rivi tekstiä ja asetetaan se muuttujaan nimi
String nimi = lukija.nextLine();
System.out.println(nimi);
Mikä on nimesi? Venla Venla
Merkkijonoja voi myös yhdistellä. Jos plus-operaatiota +
sovelletaan kahden merkkijonon välille, syntyy uusi merkkijono, jossa kaksi merkkijonoa on yhdistetty. Huomaa nokkela välilyönnin käyttö lauseen "muuttujien" osana!
String tervehdys = "Hei ";
String nimi = "Lilja";
String hyvastely = " ja näkemiin!";
String lause = tervehdys + nimi + hyvastely;
System.out.println(lause);
Hei Lilja ja näkemiin!
Jos toinen operaation +
kohteista on merkkijono, muutetaan myös toinen operaation kohteista merkkijonoksi. Alla olevassa esimerkissä kokonaisluku 2
on muutettu merkkijonoksi "2", ja siihen on yhdistetty merkkijono.
String teksti = "tuossa on kokonaisluku";
System.out.println(teksti + " --> " + 2);
System.out.println(2 + " <-- " + teksti);
tuossa on kokonaisluku —> 2 2 <— tuossa on kokonaisluku
Aiemmin tutuksi tulleet laskusäännöt sekä sulkeiden noudattaminen pätee myös merkkijonoja käsiteltäessä.
String teksti = " oho!";
System.out.println("Neljä: " + (2 + 2) + teksti);
System.out.println("Mutta! kaksikymmentäkaksi: " + 2 + 2 + teksti);
Neljä: 4 oho! Mutta! kaksikymmentäkaksi: 22 oho!
Seuraavassa on ensimmäisestä osasta tuttu käyttäjää tervehtivä ohjelma pääohjelmarungon kanssa. Ohjelman nimi on Tervehdys.
import java.util.Scanner;
public class Tervehdys {
public static void main(String[] args) {
Scanner lukija = new Scanner(System.in);
System.out.print("Kenelle sanotaan hei: ");
String nimi = lukija.nextLine();
System.out.println("Hei " + nimi);
}
}
Kun yllä oleva ohjelma ajetaan, pääset kirjoittamaan syötteen. NetBeansin tulostusvälilehti näyttää ajetun ohjelman jälkeen seuraavalta (käyttäjä syöttää nimen "Venla").
Merkkijonojen vertailu ja equals
Merkkijonoja ei voi vertailla yhtäsuuri kuin operaatiolla ==
. Merkkijonojen vertailuun käytetään erillistä equals
-komentoa, joka liittyy aina verrattavaan merkkijonoon.
String teksti = "kurssi";
if (teksti.equals("marsipaani")) {
System.out.println("Teksti-muuttujassa on teksti marsipaani.");
} else {
System.out.println("Teksti-muuttujassa ei ole tekstiä marsipaani.");
}
Komento equals
liitetään aina siihen verrattavaan tekstimuuttujaan, "tekstimuuttuja piste equals teksti". Tekstimuuttujaa voidaan myös verrata toiseen tekstimuuttujaan.
String teksti = "kurssi";
String toinenTeksti = "pursi";
if (teksti.equals(toinenTeksti)) {
System.out.println("Samat tekstit!");
} else {
System.out.println("Eri tekstit!");
}
Merkkijonoja vertailtaessa on syytä varmistaa että verrattavalla tekstimuuttujalla on arvo. Jos muuttujalla ei ole arvoa, ohjelma tuottaa virheen NullPointerException, joka tarkoittaa ettei muuttujan arvoa ole asetettu tai se on tyhjä (null).
Seuraavassa käännetään !
:n eli negaatio-operaation avulla ehdon arvo päinvastaiseksi:
System.out.println("Eihän merkkijono ole 'maito'");
String merkkijono = "piimä";
if (!(merkkijono.equals("maito"))) { // tosi jos ehto merkkijono.equals("maito") on epätosi
System.out.println("ei ollut!");
} else {
System.out.println("oli");
}
ei ollut!
Negaatio-operaatio, eli !ehto
, kääntää siis totuusarvon ympäri.
int eka = 1;
int toka = 3;
boolean onkoSuurempi = eka > toka;
if (!onkoSuurempi) {
System.out.println("1 ei ole suurempi kuin 3");
}
1 ei ole suurempi kuin 3
Merkkijonoilta voi kysyä niiden pituutta kirjoittamalla merkkijonon perään .length()
eli kutsumalla merkkijonolle sen pituuden kertovaa metodia.
String banaani = "banaani";
String kurkku = "kurkku";
String yhdessa = banaani + kurkku;
System.out.println("Banaanin pituus on " + banaani.length());
System.out.println("Kurkku pituus on " + kurkku.length());
System.out.println("Sanan " + yhdessa + " pituus on " + yhdessa.length());
Edellä kutsutaan metodia length()
kolmelle eri merkkijonolle. Kutsu banaani.length()
kutsuu nimenomaan merkkijonon banaani
pituuden kertovaa metodia, kun taas kurkku.length()
on merkkijonon kurkku
pituuden kertovan metodin kutsu. Pisteen vasemman puoleinen osa kertoo kenen metodia kutsutaan.
Toistolauseen käyttö merkkijonojen kanssa käy samalla tavalla kuin muiden muuttujien kanssa. Alla olevassa esimerkissä luetaan käyttäjältä merkkijonoja, kunnes käyttäjä syöttää tyhjän merkkijonon (eli painaa vain enteriä). Tämän jälkeen tulostetaan pisin merkkijono sekä pisimmän merkkijonon pituus.
Scanner lukija = new Scanner(System.in);
String pisin = "";
while (true) {
System.out.println("Syötä sana, tyhjä lopettaa.");
String syote = lukija.nextLine();
if (syote.equals("")) {
break;
}
if (pisin.length() < syote.length()) {
pisin = syote;
}
}
if (pisin.length() > 0) {
System.out.println("Pisin merkkijono: " + pisin + " (pituus: " + pisin.length() + ")");
} else {
System.out.println("Ei järkeviä syötteitä...");
}
Merkkijonon osa
Merkkijonosta halutaan usein lukea jokin tietty osa. Tämä onnistuu mekkkijonojen eli String-luokan metodilla substring
. Metodia substring
voidaan käyttää kahdella tavalla: yksiparametrisenä palauttamaan merkkijonon loppuosa tai kaksiparametrisena palauttamaan parametrien määrittelemä osajono merkkijonosta:
String kirja = "Kalavale";
System.out.println(kirja.substring(4));
System.out.println(kirja.substring(2, 6));
vale lava
Koska substring
-metodin paluuarvo on String
-tyyppinen, voidaan metodin paluuarvo ottaa talteen String-tyyppiseen muuttujaan loppuosa.
String kirja = "8 veljestä";
String loppuosa = kirja.substring(2);
System.out.println("7 " + loppuosa); // tulostaa: 7 veljestä
Merkkijonosta etsiminen
String-luokan metodit tarjoavat myös mahdollisuuden etsiä tekstistä tiettyä sanaa. Esimerkiksi sana "erkki" sisältyy tekstiin "merkki". Metodi indexOf()
etsii sille parametrina annettua sanaa merkkijonosta. Jos sana löytyy, metodi indexOf()
palauttaa sanan ensimmäisen kirjaimen indeksin, eli paikan (muista että paikkanumerointi alkaa nollasta!). Jos taas sanaa ei merkkijonosta löydy, metodi palauttaa arvon -1.
String sana = "merkkijono";
int indeksi = sana.indexOf("erkki"); //indeksin arvoksi tulee 1
System.out.println(sana.substring(indeksi)); //tulostetaan "erkkijono"
indeksi = sana.indexOf("jono"); //indeksin arvoksi tulee 6
System.out.println(sana.substring(indeksi)); //tulostetaan "jono"
indeksi = sana.indexOf("kirja"); //sana "kirja" ei sisälly sanaan "merkkijono"
System.out.println(indeksi); // tulostetaan -1
System.out.println(sana.substring(indeksi)); // virhe!
Metodille indexOf
voi antaa haettavan merkkijonon lisäksi parametrina myös indeksin, mistä lähtien merkkijonoa haetaan. Esimerkiksi
String sana = "merkkijono";
int indeksi = sana.indexOf("erkki"); // indeksin arvoksi tulee 1
System.out.println(sana.substring(indeksi)); //tulostetaan "erkkijono"
indeksi = sana.indexOf("erkki", 2); // indeksin arvoksi tulee -1 sillä erkkiä ei löydy lopusta
System.out.println(sana.substring(indeksi)); // tapahtuu virhe!
Merkkijonojen pilkkominen pienempiin osiin
Merkkijonon pilkkominen useampaan osaan tapahtuu merkkijonon tarjoamalla metodilla split
, jolle annetaan parametrina merkkijono, jonka kohdalta käsiteltävä merkkijono jaetaan osiin. Tässä esimerkissä merkkijono jaetaan palasiin \\s+
:n mukaan, joka on säännöllinen lauseke (engl. regular expression) ja sisältää kaikki "tyhjät merkit" eli välilyönnit, rivinvaihdot, tabulaattorimerkit jne. Metodi palauttaa taulukon, joka sisältää merkkijonoja. Taulukon indekseissä on pilkotun merkkijonon osat. Metodi toimii seuraavasti:
String merkkijono = "eka toka kolmas neljäs";
String[] palat = merkkijono.split("\\s+");
System.out.println(palat[0]);
System.out.println(palat[1]);
System.out.println(palat[2]);
System.out.println(palat[3]);
int indeksi = 0;
while (indeksi < palat.length) {
System.out.println(palat[indeksi]);
indeksi = indeksi + 1;
}
eka toka kolmas neljäs eka toka kolmas neljäs
Merkkijonojen pilkkominen on erityisesti hyödyllistä silloin kun käsitellään määrämuotoista tietoa. Määrämuotoisella tiedolla tarkoitetaan tietoa, joka noudattaa jotain tiettyä säännönmukaista muotoa. Tällaisia muotoja ovat esimerkiksi tab separated format (tsv
) missä arvot ovat eritelty toisistaan sarkainmerkeillä, sekä comma separated format (csv
) missä arvot on eritelty toisistaan pilkuilla. Alla on esimerkki csv-muotoisesta nimiä ja ikiä sisältävästä tiedosta. Ensimmäinen sarake sisältää nimen ja toinen iän. Sarakkeet on eroteltu toisistaan pilkuilla.
etunimi,ika anton,2 leevi,2 lilja,1
Oletetaan, että käyttäjä syöttää yllä olevat tiedot ohjelmaan riveittäin. Syötteen lukeminen lopetetaan tyhjällä merkkijonolla. Ohjelma, joka laskisi syötettyjen henkilöiden keski-iän voidaan toteuttaa seuraavasti.
Scanner lukija = new Scanner(System.in);
int ikienSumma = 0;
int ikienLukumaara = 0;
while (true) {
String luettu = lukija.nextLine();
if (luettu.equals("")) {
break;
}
String[] palat = luettu.split(",");
ikienSumma = ikienSumma + Integer.valueOf(palat[1]);
ikienLukumaara = ikienLukumaara + 1;
}
if (ikienLukumaara > 0) {
System.out.println("Ikien keskiarvo: " + (1.0 * ikienSumma / ikienLukumaara));
} else {
System.out.println("Ei syötteitä.");
}
leevi,2 lilja,1 Ikien keskiarvo: 1.5
Vastaavalla tavalla voisi myös toteuttaa ohjelman, joka eriyttää nimet luettavasta tiedosta. Alla olevassa esimerkissä nimet säilötään listaan, jonka sisältö tulostetaan ohjelman lopuksi.
Scanner lukija = new Scanner(System.in);
ArrayList<String> nimet = new ArrayList<>();
while (true) {
String luettu = lukija.nextLine();
if (luettu.equals("")) {
break;
}
String[] palat = luettu.split(",");
nimet.add(palat[0]);
}
for (String nimi: nimet) {
System.out.println(nimi);
}
anton,2 leevi,2 lilja,1 anton leevi lilja
Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!