Pattern Matching – Java im Vergleich zu Perl

3 Kommentare

„Perl is born to pattern match.“ – Ich denke das kann man ohne Übertreibung so schreiben. Perl löst dieses Problem einfach extrem effizient und elegant. Das folgende kleine Skript zeigt ein paar Beispiele für reguläre Ausdrücke und deren Anwendung in Perl. Wobei ich hoffe, dass mich nicht gleich ein Blitz erschlägt, da ich es wage Perl-Code in unseren Blog zu schreiben, der ja ansonsten eher von Java dominiert wird ;-).

#!/usr/bin/perl -w
 
$sampleText = <<END;
Here is some text that will be used for pattern matching in this example.
Of course we need some nice outstanding words to match and some \\special
character and here some 1234 Number that will just do fine. And more ...
END
 
print "Complete Text:\n";
print $sampleText;
print "\n";
 
#
# Let's match something easy like the word "outstanding"
#
if ($sampleText =~ /(outstanding)/) {
    print "Pattern found: " . $1 . "\n\n";
}
 
#
# Let's match two expressions one being a number
#
if ($sampleText =~ /(\d+)\s+(Number)/) {
    print "Pattern found: " . $1 . $2 . "\n\n";
}
 
#
# Let's match something a bit more complicated like \\special
#
if ($sampleText =~ /(\\special)/) {
    print "Pattern found: " . $1 . "\n\n";
}
 
#
# Let's match something ignoring the case and that is the first word of
# the input string.
#
if ($sampleText =~ /^(here)/i) {
    print "Pattern found: " . $1 . "\n\n";
}
 
#
# Let's replace all occurrences of the word "and" with "NOAND"
# (without the \s+ we would also change the "and" in outst-and-ing)
#
if ($sampleText =~ s/(\s+)(and)(\s+)/$1NOAND$3/gi) {
    print "Changed Text:\n" . $sampleText . "\n\n";
}

In Java gestaltet sich die ganze Sache schon ein weniger schwieriger, da an dieser Stelle die strenge Objektorientierung etwas unhandlich ist. Es werden die Klassen Pattern und Matcher benötigt. Dabei lehnt sich insbesondere die Pattern-Klasse eng an die Perl-Implementierung an, übernimmt die entsprechenden Ausdrücke und bietet auch passende Operatoren an, z.B. Pattern.CASE_INSENSITIVE für den i-Operator und Pattern.MULTILINE für den m-Operator. Der g-Operator auf der anderen Seite wird durch die Methode replaceAll(…) der Matcher-Klasse nachgebildet.

Das obige Skript als Java-Programm sieht damit wie folgt aus:

import java.util.regex.Pattern;
import java.util.regex.Matcher;
 
public class PMatch {
 
	public String sampleText = "Here is some text that will be used for"
			+ " pattern matching in this example.\n"
			+ "Of course we need some nice outstanding words to match"
			+ " and some \\special\n"
			+ "character and here some 1234 Number that will just do"
			+ " fine. And more ...";
 
	public void printText() {
		System.out.println("Complete Text:\n" + sampleText + "\n");
	}
 
	public void matchStandardText() {
		Pattern p = Pattern.compile("(outstanding)");
		Matcher m = p.matcher(sampleText);
		if (m.find()) {
			System.out.println("Pattern found: " + m.group(1) + "\n");
		}
	}
 
	public void matchTwoExpressions() {
		Pattern p = Pattern.compile("(\\d+)\\s+(Number)");
		Matcher m = p.matcher(sampleText);
		if (m.find()) {
			System.out.println("Pattern found: " + m.group(1) + m.group(2)
					+ "\n");
		}
	}
 
	public void matchSecialChar() {
		Pattern p = Pattern.compile("(\\\\special)");
		Matcher m = p.matcher(sampleText);
		if (m.find()) {
			System.out.println("Pattern found: " + m.group(1) + "\n");
		}
	}
 
	public void matchIgnoreCase() {
		Pattern p = Pattern.compile("^(here)", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(sampleText);
		if (m.find()) {
			System.out.println("Pattern found: " + m.group(1) + "\n");
		}
	}
 
	public void replace() {
		Pattern p = Pattern.compile("(\\s+)(and)(\\s+)",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(sampleText);
		if (m.find()) {
			sampleText = m.replaceAll(m.group(1) + "NOAND" + m.group(3));
			System.out.println("Changed Text:\n" + sampleText);
		}
	}
 
	public static void main(String[] args) {
		PMatch pMatch = new PMatch();
 
		pMatch.printText();
		pMatch.matchStandardText();
		pMatch.matchTwoExpressions();
		pMatch.matchSecialChar();
		pMatch.matchIgnoreCase();
		pMatch.replace();
	}
}

Die Ähnlichkeit in der Anwendung wird hier gut sichtbar. Es muss nur bedacht werden, dass ein String in Java immer ein String bleibt und daher ein „\“ mit einem „\“ escaped werden muss. Dies führt dann zu Ausdrücken wie Pattern.compile(„(\\\\special)“), ist aber letzten Endes kein Problem. Die Ausgabe der Programme ist in beiden Fällen identisch:

Complete Text:
Here is some text that will be used for pattern matching in this example.
Of course we need some nice outstanding words to match and some \special
character will just do fine. And more ...
 
Pattern found: outstanding
 
Pattern found: 1234Number
 
Pattern found: \special
 
Pattern found: Here
 
Changed Text:
Here is some text that will be used for pattern matching in this example.
Of course we need some nice outstanding words to match NOAND some \special
character will just do fine. NOAND more ...

Heisst es also besser zu Perl zu greifen für Applikationen, die viel mit Pattern Matching arbeiten müssen? Nein, es gibt auch eine Alternative für Java-Fans und die lautet Groovy. Denn Groovy unterstützt eine Syntax die wirklich denkbar nah an Perl herankommt. Eine Idee wie das aussieht bekommt man hier. Auf der anderen Seite ist so ein kleines Perl-Skript ab und zu ja durchaus auch nicht zu verachten :-).

Thomas Jaspers

Langjährige Erfahrung in agilen Softwareprojekten unter Einsatz von Java-Enterprise-Technologien. Schwerpunkt im Bereich der Testautomatisierung (Konzepte und Open-Source Werkzeuge).

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Frankie

    20. November 2009 von Frankie

    danke für die Gegenüberstellung, wenn man Perl gewohnt ist, kann man damit recht schnell sehen, wie es mit Java geht (und das ist tatsächlich nicht gerade elegant…)

  • Thomas Jaspers

    23. November 2009 von Thomas Jaspers

    Perl ist ungeschlagen was Pattern Matching angeht und ich brauche da für Java auch immer wieder eine Gedankenstütze und daher auch der Blogbeitrag 🙂 … zumindest auf dem Papier sieht Groovy aber auch nicht schlecht aus, muss ich mir mal in einer ruhigen Minute anschauen.

  • Bernhard Schmalhofer

    Als Perl Fan freue ich mich wenn Perl auch mal außerhalb der Echokammer auftritt. Bei den Perl Regexen gibt es natürlich noch einen große Zahl von Features die in anderen Sprachen nur bedingt verfügbar sind. Ein paar Beispiele habe ich hier aufgeführt.


    #!/usr/bin/env perl

    use strict;
    use warnings;
    use feature qw(say);

    my $sample_text = <<'END';
    Here is some text that will be used for pattern matching in this example.
    Of course we need some nice outstanding words to match and some \special
    character and here some 1234 Number that will just do fine. And more ...
    END

    say 'Complete Text:';
    say $sample_text;

    # Let's match something easy like the word "outstanding"
    if ( $sample_text =~ m/(outstanding)/ ) {
    say "Pattern found: $1";
    say '';
    }

    # naming captures is often a good idea
    if ( $sample_text =~ m/(?nice)\s+(?outstanding)/ ) {
    say "Patterns found with named captures: $+{adverb} $+{adjectiv}";
    say '';
    }

    # Let's match two expressions one being a number
    if ( $sample_text =~ m/(\d+)\s+(Number)/ ) {
    say "Pattern found: $1$2";
    say '';
    }

    # regexes can also be composed out of more simple parts
    # and they can be documented
    my $integer_match = qr(\d+);
    if (
    $sample_text =~ m/
    ($integer_match) # an integer captured into $1
    \s+ # seperatated by whitespace
    (Number) # designated by a type, captured into $2
    /x
    )
    {
    say "Composed and commented pattern found: $1$2";
    say '';
    }

    # Let's match something a bit more complicated like \special
    # in regexes '\' needs to be quoted
    if ( $sample_text =~ m/(\\special)/ ) {
    say "Pattern found: $1";
    say '';
    }

    # Let's match something ignoring the case and that is the first word of
    # the input string.
    if ( $sample_text =~ m/^(here)/i ) {
    say "Pattern found: $1";
    say '';
    }

    # Let's replace all occurrences of the word "and" with "NOAND"
    # \b forces anchoring at word boundaries, so that 'outstanding' won't be affected
    if ( $sample_text =~ s/\band\b/NOAND/gi ) {
    say "Changed Text:";
    say $sample_text;
    say '';
    }

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.