Innholdsoversikt for programutvikling

Tekstmanipulering

Datatypen String

Fra før vet vi at vi bruker datatypen String for variable som skal inneholde tekst. Vi skal nå gjøre oss kjent med noen av metodene i String-klassen og se eksempler på hvordan de kan brukes til å undersøke og manipulere tekst.

Konstruktører

Vi vet at vi kan opprette String-objekter i form av String-literaler som i følgende eksempel:

  String navn = "Snorre Sturlason";

Vi kan imidlertid også opprette String-objekter på vanlig måte ved bruk av new-operatoren og en konstruktør. I String-klassen er det definert mange forskjellige konstruktører. De mest aktuelle er følgende:

public String()
Oppretter en ny streng med verdi "".
public String(String kilde)
Oppretter en ny streng som er en kopi av strengen kilde
public String(char[] kilde)
Oppretter en ny streng med de tegn som arrayen kilde inneholder.
public String(char[] kilde, int startpos, int antall)
Oppretter en ny streng med de tegn som en delarray av arrayen kilde inneholder. Parameteren startpos angir indeksposisjonen som første tegn skal hentes fra i kildearrayen, mens parameteren antall angir hvor mange tegn som skal hentes.

Instruksjonen

  String abc = "abc";

er likeverdig med instruksjonene

  char data[] = {'a', 'b', 'c'};
  String abc = new String(data);

String-objekter er indeksert fra 0 og oppover, på tilsvarende måte som array-objekter. Enkelttegnene i en tekst er av type char, som er en av de såkalte primitive (grunnleggende) datatypene i Java-språket. For å hente ut de enkelte tegnene kan vi bruke String-metoden charAt:

  String s = ...;
  int indeks = ...;
  char tegn = s.charAt(indeks);

Som det er tilfelle for array-objekter, kjenner også String-objekter sin egen lengde (antall tegn):

  int lengde = s.length();

Legg merke til at for String-objekter er length en metode, mens det for arrayer er et datafelt:

  int[] array = ...;
  int arraylengde = array.length;

Dersom vi i en String-metode bruker en indeksposisjon som er mindre enn null eller større enn length() - 1, så vil det bli kastet ut en IndexOutOfBoundsException.

Et tegn, verdi av type char, er i Java kodet etter systemet Unicode, som bruker 2 byter (16 biter) for hvert tegn.

String-metoder

Følgende opplisting gir en kort oversikt over de viktigste String-metodene og deres virkemåte. Fullstendig oversikt kan du finne ved å klikke på linken String. Oversikten inneholder også noen metoder som kan brukes til å undersøke tegn. Disse finnes i klassen Character. Navnene på metodene er stort sett selvforklarende.

Eksempel på noen String-metoder og hva de returnerer

String s1 = "Eksempel";
String s2 = "eksempel";

INSTRUKSJON                        RETUR
s1.charAt(2);                      's'
s1.substring(3);                   "empel"
s1.substring(3, 6);                "emp"
s1.length();                       8
s1.indexOf('e');                   3  //første forekomst
s1.lastIndexOf('e');               6  //siste forekomst
s2.indexOf('e', 2);                3  //starter søket i indeksposisjon 2
s1.indexOf("sem");                 2
s1.indexOf("mep");                 -1  //finnes ikke
s1.startsWith("eks");             false
s1.endsWith("pel");                true
s1.toLowerCase();                  "eksempel"  //s1 er uendret!
s1.toUpperCase();                  "EKSEMPEL"  //s1 er uendret!
s1.equals(s2);                     false
s1.equalsIgnoreCase(s2);           true
s1.compareTo(s2);                  negativt heltall
s2.compareTo(s1);                  positivt heltall
s1.compareToIgnoreCase(s2);        0
s1.replace('e', 'E');              "EksEmpEl"  //s1 er uendret!

Annen nyttig String-metode:

trim()   Returnerer en streng der eventuelle blanke tegn foran og bak i
         strengen er fjernet.

Nyttige static-metoder i klassen Character

Eksempel

char t = ...;   //gi verdi til t

if (Character.isDigit(t)) ...
if (Character.isLetter(t)) ...
if (Character.isLetterOrDigit(t)) ...
if (Character.isLowerCase(t)) ...
if (Character.isUpperCase(t)) ...
if (Character.isSpaceChar(t)) ...
if (Character.isWhiteSpace(t)) ...

char s = Character.toUpperCase(t);
char c = Character.toLowerCase(s);

Konvertere tegn til tilsvarende String:

String s = (new Character(t)).toString();  //toString er ikke static

Merk deg at String-objekter er såkalt immutable: når de først er opprettet, så kan de ikke endres. String-metodene for omforming av String-objekter returnerer derfor et nytt String-objekt med den ønskete omformingen. Dersom en streng skal bygges opp av mange småbiter, vil det være ineffektivt å opprette et stort antall String-objekter som ikke har annen oppgave enn å være et ledd i en oppbyggingsprosess. I slike tilfelle kunne vi under byggeprosessen ønske oss en type dynamisk streng. Det har vi i klassen StringBuilder som er beskrevet i neste avsnitt.

Konstruering av strenger

Når vi skal bygge opp en streng av mange småbiter, vil det istedenfor å skjøte sammen bitene ved hjelp av +-operatoren være mer effektivt å bygge opp et StringBuilder-objekt som vi så konverterer til String til slutt. Objektet kan vi opprette som et i utgangspunktet tomt objekt på følgende måte:

  StringBuilder bygger = new StringBuilder();

Alternativt kan vi supplere konstruktøren med en String-parameter som gir en startstreng som utgangspunkt. Videre kan vi skjøte til enten enkelttegn (av type char) eller strenger:

  bygger.append(tegn);
  bygger.append(streng);

Det finnes også mange andre varianter av append-metoden for tilføyelse av forskjellig type data. Dessuten er det mange muligheter for å legge inn data innimellom det som allerede eksisterer ved å bruke de forskjellige variantene til insert-metoden. Når vi er ferdige med å bygge opp StringBuilder-objektet, kan vi få hentet ut en streng som inneholder de data vi har lagt inn:

  String ferdigstreng = bygger.toString();

Sammenlikning av strenger

I oversikten ovenfor er det fire metoder som har med sammenlikning av strenger å gjøre: equals, equalsIgnoreCase, compareTo og compareToIgnoreCase. De to første sammenlikner innholdet i String-objektene de brukes på. Metoden equals returnerer true bare i tilfelle strengene inneholder nøyaktig de samme tegnene i samme rekkefølge, det vil si dersom tegnene har de samme Unicode-kodene. I metoden equalsIgnoreCase gjøres det ikke noe skille mellom små og store bokstaver. For eksempel vil Ë og ë bli regnet som like, men forskjellige fra E og e. Andre tegn enn bokstaver, for eksempel skilletegn, blir bare regnet for å være lik seg selv.

Metodene compareTo og compareToIgnoreCase har med rekkefølge å gjøre, nærmere bestemt det som kalles leksikografisk rekkefølge. Det som sammenliknes, er tegnenes Unicode-verdier. Leksikografisk rekkefølge er definert på denne måten: Dersom to strenger er forskjellige, så har de enten forskjellige tegn i en indeksposisjon som er gyldig for begge strengene, eller så har de forskjellig lengde, eller begge deler. Dersom de har forskjellige tegn i én eller flere indeksposisjoner, så la k være den minste av disse indeksposisjonene. Da vil den strengen som har det minste tegnet i indeksposisjon k, bestemt ved å bruke sammenlikningsoperatoren < på tegnenes Unicode-verdier, være den strengen som kommer først i leksikografisk rekkefølge. I dette tilfelle vil instruksjonen

  streng1.compareTo( streng2 )

returnere verdien

  streng1.charAt(k) - streng2.charAt(k)

Dersom strengene ikke er forskjellige i noen av indeksposisjonene som er gyldige for begge, vil den korteste strengen være den som kommer først i leksikografisk rekkefølge. I dette tilfelle vil compareTo returnere lengdeforskjellen mellom strengene, det vil si verdien

  streng1.length() - streng2.length()

Som navnet indikerer, vil compareToIgnoreCase ikke skille mellom store og små bokstaver, tilsvarende som for equalsIgnoreCase.

Merk at metodene compareTo og compareToIgnoreCase ikke tar noen hensyn til nasjonale særegenheter. For eksempel vil metodene ikke behandle riktig rekkefølgen for de norske bokstavene Æ, Ø og Å, samt de små variantene av disse. Metoden compareTo vil dessuten regne alle store bokstaver for å komme foran alle små, slik at for eksempel Z regnes å komme foran a. Hvordan vi skal få foretatt riktig sortering av strenger, slik at det også tas hensyn til disse tingene, er beskrevet i notatet Sortering av strenger.

Merk for øvrig at sammenlikningsoperatoren == brukt på String-objekter sammenlikner objektenes peker-verdier (referanser). Med andre ord så sjekkes det om det blir referert til det samme objektet. Som regel er det ikke dette vi er interessert i å sjekke, men om to forskjellige objekter har samme innhold. For å sjekke dette er det equals eller equalsIgnoreCase vi må bruke. Merk ellers at

  streng1.equals(streng2)

alltid vil ha samme verdi som

  streng1.compareTo(streng2) == 0

Det samme er tilfelle med

  streng1.equalsIgnoreCase(streng2)

og

  streng1.compareToIgnoreCase(streng2) == 0

Regulære uttrykk

Regulære uttrykk (engelsk: Regular expressions) er tekststrenger som har som oppgave å beskrive et mønster. De gir oss et verktøy for å definere samlinger (mengder) av tekststrenger, slik at en tekststreng tilhører samlingen dersom den har det gitte mønsteret, ellers ikke. Regulære uttrykk kan brukes til å sjekke om en gitt tekststreng har det mønsteret som uttrykket definerer. De kan også brukes på en tekststreng til å erstatte med en annen tekststreng alle delstrenger som har det gitte mønsteret. Og de kan brukes til å splitte en tekststreng i enkeltkomponenter som er atskilt av det gitte mønsteret. (Se avsnittet Splitte opp tekst i enkeltkomponenter lenger ute.) Her skal vi ta for oss reglene for å bygge opp regulære uttrykk, samt beskrive noen forhåndsdefinerte mønstre.

Innledende eksempler: Sammenlikning av tekststrenger

Fra String-klassen kjenner vi metoden equals for å sjekke om to strenger har samme innhold. I String-klassen finner vi imidlertid også metoden matches med følgende beskrivelse:

  public boolean matches(String regex)

Tells whether or not this string matches the given regular expression. An invocation of this method of the form str.matches(regex) yields exactly the same result as the expression

  Pattern.matches(regex, str)
Parameters:
regex - the regular expression to which this string is to be matched
Returns:
true if, and only if, this string matches the given regular expression
Throws:
PatternSyntaxException - if the regular expression's syntax is invalid

Begge instruksjonene

  "Java".matches("Java");
  "Java".equals("Java");

vil som resultat gi true. Men matches-metoden har større potensiale. Den kan i tillegg til å sjekke om en streng er lik en bestemt annen streng, også sjekke om strengen har et bestemt mønster. For eksempel vil alle følgende tre instruksjoner gi true som resultat:

  "Java er morsomt".matches("Java.*");
  "Java er kult".matches("Java.*");
  "Java kan brukes til mye".matches("Java.*");

I de tre instruksjonene ovenfor er "Java.*" eksempel på et regulært uttrykk. Det beskriver et mønster som begynner med Java og som fortsetter med et vilkårlig antall tegn (eller ingen slike). Delstrengen ".*" vil i dette tilfelle stemme overens med ("matche") en streng bestående av et vilkårlig antall tegn, eller ingen slike (en tom streng).

Metategn

I det regulære uttrykket "Java.*" tolkes punktumet "." som et såkalt metategn, det vil si som et tegn med en spesiell betydning når testen på matching skal foretas. Metategnet "." tolkes som "et hvilket som helst tegn". Stjerna "*" er også et metategn, nærmere bestemt det som kalles en kvantor: den angir et antall. Kvantoren "*" står for antallet "null eller flere".

Følgende metategn kan brukes i forbindelse med matching: ([{\^-$|]})?*+.

I enkelte situasjoner kan det tenkes at vi ikke ønsker at noen av disse tegnene skal tolkes som metategn, men som et vanlig tegn. Det finnes to muligheter for å oppnå dette:

Hvordan definere mønstre

Følgende tabell gir noen eksempler på hvordan mønstre kan defineres i form av delmengder av tegn. I venstre kolonne står det regulære uttrykket, mens det i høyre kolonne er beskrevet hvilke tegn som vil "matche" uttrykket.

Delmengder av tegn
[abc] a, b, eller c
[^abc] Hvilket som helst tegn, unntatt a, b, eller c (negasjon)
[a-zA-Z] a til og med z, eller A til og med Z, (intervall)
[a-d[m-p]] a til og med d, eller m til og med p: [a-dm-p] (union)
[a-z&&[def]] d, e, eller f (snitt)
[a-z&&[^bc]] a til og med z, unntatt b og c: [ad-z] (subtraksjon)
[a-z&&[^m-p]] a til og med z, men ikke m til og med p: [a-lq-z] (subtraksjon)

Forhåndsdefinerte klasser av tegn

Ordet "klasse" har i dette tilfelle ikke noe med javaklasser å gjøre, men betegner en samling eller mengde. Følgende tabell gir en oversikt over de forhåndsdefinerte klassene. Symbolene i venstre kolonne er en forkortelse for den samling som er beskrevet i høyre kolonne.

Forhåndsdefinerte klasser av tegn
. Et hvilket som helst tegn
\d Et siffer: [0-9]
\D Et ikke-siffer: [^0-9]
\s Et "whitespace"-tegn: [ \t\n\x0B\f\r]
\S Et synlig tegn (ikke "whitespace"): [^\s]
\w Et "ord-tegn": [a-zA-Z_0-9]
\W Et "ikke-ord-tegn": [^\w]

Det blir anbefalt å bruke de forhåndsdefinerte klassene av tegn så sant som det er mulig. Det gjør koden mer lesbar og bidrar dessuten til å eliminere feil som kan ha oppstått på grunn av feil konstruksjon av regulære uttrykk.

Uttrykt i ord kan vi summere opp tabellen på følgende måte:

Tilsvarende koder, men med stor bokstav, matcher det motsatte:

Merknad I Java-språket indikerer bakoverskråstrek \ starten på det som kalles en escape-sekvens. Dersom vi ønsker at en slik skråstrek skal inngå som en del av en konkret streng, det som i Java kalles en String-literal, må vi skrive to slike skråstreker etter hverandre. Dette må vi ta hensyn til dersom symbolene i tabellen ovenfor skal inngå i en String-literal. For eksempel vil strengen "\\d" representere det regulære uttrykket \d.

Kvantorer

I de innledende eksemplene ble stjerne "*" omtalt som en kvantor. Det finnes også en del andre kvantorer. Vi skal bare ta for oss de viktigste av disse. Følgende tabell gir en oversikt.

KvantorBetydning
X?X, én gang, eller ingen
X*X, null eller flere ganger
X+X, én eller flere ganger
X{n}X, nøyaktig n ganger
X{n,}X, minst n ganger
X{n,m}X, minst n ganger, men ikke mer enn m ganger

Merk Kvantorene skal skrives uten bruk av mellomrom (blanke tegn). For eksempel kan X{n,m} ikke skrives som X{n, m} med et mellomrom etter kommaet.

Merknad Det er tillatt å bruke parenteser til å gruppere mønstre. For eksempel vil (ab){3} svare til ababab, mens ab{3} vil svare til abbb.

Eksempler

Eksempel 1
En streng bestående av 11 sifre (for eksempel et kontonummer) kan beskrives slik:

  "\\d{11}"

Eksempel 2
Et oddetall ender på et av sifrene 1, 3, 5, 7 eller 9 og består av minst ett siffer. Det kan beskrives slik (når det er uten fortegn):

  "\\d*[13579]"

Eksempel 3
Retningsnummer til Norge (iallfall for mobiltelefoner) blir angitt med +47. Et mobilnummer på 8 sifre og med et slikt retningsnummer foran kan derfor beskrives slik:

  "\\+47\\d{8}"

Splitte opp tekst i enkeltkomponenter

Som verktøy for å splitte opp strenger i enkeltkomponenter, blir det for ny kode anbefalt å bruke String-klassens split-metode eller noen av verktøyene i pakken java.util.regex. Det er også mulig å bruke en Scanner til slik oppsplitting. En Scanner kan som kilde for det som skal splittes opp også bruke en hvilken som helst input-strøm, for eksempel en fil. Dessuten kan den gjenkjenne forskjellige datatyper. Tidligere var det objekter av klassen StringTokenizer som ble brukt til å foreta oppsplitting av tekst i enkeltkomponenter. Av hensyn til kompatibilitet bakover finnes klassen fortsatt, men anbefales ikke brukt i ny kode. Bruk av StringTokenizer-objekter er beskrevet i notatet Lese tekst ord for ord ved hjelp av en StringTokenizer.

For å definere skille mellom enkeltkomponentene i den teksten som skal splittes opp, bruker vi et regulært uttrykk. (For en StringTokenizer blir det bare brukt en streng.) Hva som menes med regulære uttrykk er beskrevet i avsnittet Regulære uttrykk ovenfor.

String-klassens split-metode

Metoden finnes i to versjoner. Som regel er det den som har bare én parameter vi har bruk for:

  public String[] split(String regex)

Parameteren regex er det regulære uttrykket som definerer skillet mellom enkeltkomponentene. Metoden returnerer en array som inneholder de enkelte komponentene som oppsplittingen har gitt. Arrayen kan være av vilkårlig størrelse. Merk deg imidlertid at oppsplittingen kan inneholde komponenter som består av tomme strenger. Men eventuelle tomme strenger på slutten vil ikke bli tatt med. Dersom den strengen som skal splittes opp ikke har noen delstreng som passer med det regulære uttrykket, vil den returnerte arrayen ha ett element og dette inneholder den strengen som skulle splittes opp. Metoden har samme virkning som å bruke den andre versjonen av metoden (se nedenfor) med grense-parameter lik null.

Eksempel 1

  String test = "Dette er en test på bruk av split-metoden.";
  String[] resultat = test.split("\\s");
  for (int i = 0; i < resultat.length; i++)
    System.out.println(resultat[i]);

vil gi følgende utskrift:

  Dette
  er
  en
  test
  på
  bruk
  av
  split-metoden.

Husk at det regulære uttrykket \s betyr såkalt whitespace. Siden uttrykket inngår i en streng når det blir brukt i metodekallet ovenfor, må det være en ekstra bakoverskråstrek foran. (Se avsnittet om regulære uttrykk.) Bindestreken og punktumet i den siste komponenten som ble skrevet ut, er ikke whitespace. Derfor får vi det ut på denne måten.

Eksempel 2
Strengen "20:00:00" resulterer i følgende oppsplitting med de regulære uttrykkene som er angitt i tabellens første kolonne:

Regulært uttrykkResultat
:{"20", "00", "00"}
0{"2", ":", "", ":"}

Legg merke til at vi i det siste tilfelle får en tom streng innimellom, men at den tomme strengen på slutten ikke blir tatt med i returen.

Den andre versjonen av split-metoden har to parametre:

  public String[] split(String regex, int grense)

Første parameter har samme betydning som i første versjon av metoden. Parameteren grense kontrollerer antall ganger det regulære uttrykket skal brukes. Parameteren vil derfor påvirke størrelsen på den returnerte arrayen. Dersom grensen n er større enn null, vil det regulære uttrykket bli brukt høyst n - 1 ganger. Den returnerte arrayen kan dermed ikke få mer enn n plasser. Siste element i arrayen vil da inneholde all tekst i kildestrengen som følger etter siste match til det regulære uttrykket. Dersom n er null eller negativ, vil det regulære uttrykket bli brukt så mange ganger som mulig og den returnerte arrayen kan ha hvilken som helst lengde. Det samme er tilfelle når n er null, men eventuelle tomme strenger på slutten vil da bli droppet.

Eksempler
Følgende eksempler viser hvordan forskjellige verdier for grenseparameteren påvirker oppsplittingen av strengen "20:00:00" med de regulære uttrykk som er angitt i tabellens første kolonne.

Regulært uttrykkGrenseResultat
:2{"20", "00:00"}
:5{"20", "00", "00"}
:-2{"20", "00", "00"}
05{"2", ":", "", ":", ""}
0-2{"2", ":", "", ":", ""}
00{"2", ":", "", ":"}

Legg merke til når den returnerte arrayen inneholder tomme strenger på slutten, og når den ikke gjør det.

Splitte opp input ved hjelp av en Scanner

En Scanner kan brukes til å splitte opp tekstlig input i enkeltkomponenter. Som input kan det brukes en streng eller en strøm, for eksempel en fil eller input fra tastaturet. Byte fra inputstrømmen blir konvertert til tegn. Det kan enten være i samsvar med default-tegnsettet til den underliggende plattform (noe som er unødvendig å spesifisere), eller i samsvar med et spesifisert tegnsett. I tillegg til å splitte opp i strengkomponenter har en Scanner evne til å gjenkjenne verdier av javas primitive datatyper (gitt på strengform), unntatt typen char, slik at den kan returnere neste verdi av en spesifisert type. Også typene BigInteger og BigDecimal kan den gjenkjenne og returnere. I numeriske verdier blir det påstått at det også kan være tusen-separator i samsvar med den formateringsstandard som gjelder på stedet (eller på en spesifisert lokalitet). I Norge er det mellomrom (blankt tegn) som er den korrekte tusen-separatoren. Strengen "32 767" skulle derfor på korrekt måte bli lest som int-verdien 32767. Det viser seg imidlertid at dette ikke stemmer. Strengen vil bli tolket som to separate heltallsverdier. Derimot blir komma på riktig måte tolket som desimaltegn i en streng som "8,5".

Dersom annet ikke er spesifisert, er det såkalt whitespace som vil bli brukt som skille mellom komponentene når oppsplittingen foretas. Men det er mulig ved hjelp av metoden useDelimiter å spesifisere hvilket som helst regulært uttrykk som skille mellom komponentene. Ønsker vi seinere å gå tilbake til default-virkemåten med bruk av whitespace som skille, kan vi oppnå det ved kall på metoden reset.

Når vi er ferdig med å bruke en Scanner, er det nødvendig å lukke den ved kall på dens close-metode for å signalisere at vi er ferdig med å bruke dens eventuelle underliggende strøm. Denne vil da også automatisk bli lukket.

Eksempel 1

I programmet Forfatternavn.java som er gjengitt nedenfor, blir det fra tekstfeltet input lest inn navn på formatet
Etternavn, Fornavn (eventuelt et eller flere mellomnavn).
Navnene skrives ut igjen i tekstfeltet output på formatet
Fornavn (eventuelt et eller flere mellomnavn) Etternavn.

Eksempel: Innlest navn
Beauvoir, Simone de
blir skrevet ut igjen som
Simone de Beauvoir

I programmet er navnene kalt forfatternavn, men det kan selvsagt være hvilke som helst andre navn på samme format. Driverklasse for programmet finnes i fila Tokentest.java

Innlesing, konvertering og utskrift av navn skjer i metoden konverterNavn. Den splitter opp det innleste navnet ved hjelp av en Scanner der komma og mellomrom oppfattes som skille mellom ordene. Først trekkes etternavnet ut av den innleste strengen og lagres i en egen variabel. Deretter trekkes det ut fornavn og eventuelle mellomnavn så lenge det finnes flere. De tilføyes etter tur i en egen variabel for fornavn og mellomnavn. Til slutt blir strengen som inneholder etternavnet tilføyd, og det hele vises i tekstfeltet for utskrift. For øvrig blir det mellom de enkelte navnene lagt inn mellomrom.

 1 import javax.swing.*;
 2 import java.awt.*;
 3 import java.awt.event.*;
 4 import java.util.*;
 5
 6 public class Forfatternavn extends JFrame
 7 {
 8   private JTextField input, output;
 9
10   public Forfatternavn()
11   {
12     super( "Forfatternavn" );
13     input = new JTextField( 30 );
14     output = new JTextField( 30 );
15     output.setEditable( false );
16     Container c = getContentPane();
17     c.setLayout( new FlowLayout() );
18     c.add( new JLabel( "Forfatternavn (ettenavn, fornavn):" ) );
19     c.add( input );
20     c.add( new JLabel( "Forfatternavn (fornavn etternavn):" ) );
21     c.add( output );
22     input.addActionListener( new Inputlytter() );
23     setSize( 600, 100 );
24     setVisible( true );
25   }
26
27   public void konverterNavn()
28   {
29     String navn = input.getText();
30     Scanner leser = new Scanner( navn );
31     leser.useDelimiter("[,\\s]+");
32     String etternavn = "";
33     if ( leser.hasNext() )
34       etternavn = leser.next();
35     String fornavn = "";
36     while ( leser.hasNext() )
37       fornavn += leser.next() + " ";
38
39     leser.close();
40
41     if ( !etternavn.equals( "" ) )
42       output.setText( fornavn + etternavn );
43     else
44       output.setText( "Ingen navn lest inn." );
45   }
46
47   private class Inputlytter implements ActionListener
48   {
49     public void actionPerformed( ActionEvent e )
50     {
51       if ( e.getSource() == input )
52         konverterNavn();
53     }
54   }
55 }

Merknad I eksemplet ovenfor brukes strengen "[,\\s]+" som det regulære uttrykket som definerer skille mellom komponentene. Det betyr at skille er enten minst et blankt tegn (egentlig minst et såkalt whitespace), eller minst et komma, eventuelt etterfulgt av et antall blanke.

Bildet under viser vinduet fra en kjøring av programmet.

Eksempel 2

Programmet ScanSum som er gjengitt nedenfor er hentet fra The Java Tutorials, men litt tilpasset norske forhold. Programmet leser inn tall (i form av sifferstrenger) fra fila tall.txt og summerer dem. (Slik programmet er skrevet, med hardkoding av filsti for tallfil, må den plasseres på riktig sted for at programmet skal finne den.) Tallfila har følgende innhold:

8,5
32767
3,14159
1000000,1

Legg merke til at den inneholder en blanding av hele tall og desimaltall, og at det er brukt komma som desimaltegn. Når programmet blir kjørt, vil det skrive ut følgede resultat:

1032778.74159

Her er programmets kildekode:

 1 import java.io.FileReader;
 2 import java.io.BufferedReader;
 3 import java.io.IOException;
 4 import java.util.Scanner;
 5
 6 public class ScanSum
 7 {
 8   public static void main(String[] args) throws IOException
 9   {
10     double sum = 0;
11     try (Scanner s = new Scanner(new BufferedReader(
12             new FileReader("tall.txt"))))
13     {
14       while (s.hasNext())
15       {
16         if (s.hasNextDouble())
17           sum += s.nextDouble();
18         else
19           s.next();
20       }
21     }
22     System.out.println(sum);
23   }
24 }

Eksempel 3: Testprogram for regulære uttrykk

Dersom vi ønsker å sjekke om en gitt tekststreng er i samsvar med et bestemt mønster, er det ikke alltid så lett å finne ut hvordan vi skal skrive et korrekt regulært uttrykk for å beskrive vedkommende mønster. Programmet RegexTestHarness gjengitt nedenfor er en bearbeidet versjon av et program fra The Java Tutorials. Programmets brukervindu er vist på følgende bilde.

I første tekstfeltet skriver man inn det regulære uttrykket (mønsteret) man vil teste på. Det blir lest inn og registrert når man trykker på returtasten. I det andre tekstfeltet skriver man inn den teksten man vil det skal søkes i. Deretter trykker man på returtasten. Søket blir da utført og resultatet blir skrevet ut i tekstområdet nedenfor.

 1 import java.util.regex.*;
 2 import javax.swing.*;
 3 import java.awt.event.*;
 4 import java.awt.*;
 5
 6 public class RegexTestHarness extends JFrame
 7 {
 8   private JTextField input1, input2;
 9   private JTextArea utskrift;
10   private String reguttr, teststreng;
11   private Pattern pattern;
12   private Matcher matcher;
13
14   public RegexTestHarness()
15   {
16     input1 = new JTextField( 20 );
17     input2 = new JTextField( 30 );
18     utskrift = new JTextArea( 10, 40 );
19     utskrift.setEditable( false );
20     Container c = getContentPane();
21     c.setLayout(new FlowLayout());
22     c.add(new JLabel("Regulært uttrykk det skal testes på"));
23     c.add(input1);
24     c.add(new JLabel("Uttrykk det skal søkes i"));
25     c.add(input2);
26     c.add(new JScrollPane(utskrift));
27     Inputlytter lytter = new Inputlytter();
28     input1.addActionListener(lytter);
29     input2.addActionListener(lytter);
30     setSize( 500, 300 );
31     setVisible(true);
32   }
33
34   public static void main(String[] args)
35   {
36     new RegexTestHarness();
37   }
38
39   private class Inputlytter implements ActionListener
40   {
41     public void actionPerformed( ActionEvent e )
42     {
43       if ( e.getSource() == input1 )
44       {
45         try
46         {
47           reguttr = input1.getText();
48           pattern = Pattern.compile(reguttr);
49           utskrift.setText("");
50         }
51         catch ( PatternSyntaxException pe )
52         {
53           utskrift.setText("Syntaksfeil i regulært uttrykk!" );
54         }
55       }
56       else if ( e.getSource() == input2 )
57       {
58         try
59         {
60           teststreng = input2.getText();
61           matcher = pattern.matcher(teststreng);
62           boolean found = false;
63           while (matcher.find())
64           {
65             utskrift.append("Jeg fant tekstkomponenten " + matcher.group()
66                   + " med start i indeks " + matcher.start()
67                   + " og slutt i indeks " + matcher.end() + "\n");
68             found = true;
69           }
70           if(!found)
71           {
72             utskrift.append("Fant ikke noe som matchet.\n");
73           }
74         }
75         catch ( NullPointerException ex)
76         {
77           utskrift.setText("Mangler mønster til å sammenlikne med!");
78         }
79       }
80     }
81   }
82 }

Innholdsoversikt for programutvikling

Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2015