Využívanie regulárnych výrazov v rôznych programovacích jazykoch je veľmi zaujímavá a komplexná téma. Samotné regulárne výrazy (regular expressions) majú pomerne komplikovanú syntax s množstvom rozšírení. V štandardnom Java API veľmi dlho chýbala možnosť pracovať s regulárnymi výrazmi. Od verzie 1.4.0 však majú túto možnosť aj „javisti“. Napriek tomu, alebo práve preto, existuje viacero riešení (regular expression engines) od tretích strán, určených pre programátorov v Jave.

V tomto článku si ukážeme dve riešenia, ako využiť regulárne výrazy v spojitosti so servletmi. Prvé riešenie bude využívať možnosti, ktoré nám dáva priamo Java API, konkrétne využijeme balíček java.util.regex.*. Druhé riešenie bude postavené na knižnici OROMatcher 1.1 (použijeme nástroj PerlTools), ktorý je možné stiahnuť zo stránok pána Daniela F. Savareseho, pričom na tejto stránke nájdete aj ďalšie nástroje na prácu s textom. Verzia 1.1 je posledná verzia dostupná z jeho stránok. Pokračovateľom v tejto oblasti je projekt Jakarta – ORO, ktorý predstavuje sadu knižníc na pracovanie s textom, kompatibilnú s Perl 5 a AWK regulárnymi výrazmi. Táto knižnica je pokračovateľom nástrojov OROMatcher, AwkTools, PerlTools a TextTools, pôvodne vyvinutých spoločnosťou ORO, Inc. Mimochodom skratka ORO znamená Original Reusable Objects.

O používaní regulárnych výrazov by bolo možné napísať veľmi veľa, ale to nie je účelom tohto článku. Aby ste neboli ukrátení o informácie, tak na záver uvádzam niekoľko zaujímavých zdrojov, z ktorých môžete načerpať množstvo užitočných informácií, stiahnuť nástroje na prácu s textom alebo si dokonca otestovať funkčnosť vami vytvoreného regulárneho výrazu pomocou PHP, Perlu, AWK a podobne. Účelom tohto článku je oboznámiť vás s tým, ako jednoducho a efektívne využiť výhody regulárnych výrazov v spojení so servletmi.

Vytvoríme si servlet, ktorý bude pracovať ako filter. Úlohou servletu bude odstrániť tag plaintext zo všetkých HTML stránok predaných v požiadavke na servlet a nahradiť ho prázdnym reťazcom. Tento servlet bude využívať klasické možnosti, ktoré nám dáva J2SE, respektíve J2EE.

ServletFilter.java:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletFilter extends HttpServlet {
 public void doGet(
          HttpServletRequest req, HttpServletResponse res)
              throws ServletException, IOException {
  String contentType = req.getContentType();
  if (contentType == null) return;
  res.setContentType(contentType);
  PrintWriter out = res.getWriter();
  BufferedReader in = req.getReader();
  String line = null;
  while ((line = in.readLine()) != null) {
   line = replace(line, „<plaintext>“, „“);
   line = replace(line, „</plaintext>“, „“);
   out.println(line);
  }
 }
 public void doPost(
          HttpServletRequest req, HttpServletResponse res)
              throws ServletException, IOException {
  doGet(req, res);
 }
 private String replace(
          String line, String oldString, String newString) {
  int index = 0;
  while ((index = line.indexOf(oldString, index)) >= 0) {
   // nahradíme starý reťazec novým
   line = line.substring(0, index) + newString +
          line.substring(index + oldString.length());
   index += newString.length();
  }
  return line;
 }
}

Čo sa týka funkčnosti, myslím, že je celkom jasné, že základom je metóda replace(), ktorá nahradí jeden reťazec za iný. Problém je však v tom, že takýto postup je relatívne málo efektívny, a, čo je oveľa podstatnejšie, tento mechanizmus je case-sensitive. Znamená to, že metóda replace() zlyhá v prípade, že značky budú vyzerať napríklad takto: <Plaintext>, <PLAINTEXT>, <pLAINTEXT>. Samozrejme môžeme vytvoriť všetky možné kombinácie, ale použitie regulárneho výrazu nám značne zjednoduší a sprehľadní kód.

S využitím regulárneho výrazu môžeme prepísať servlet tak, aby nahradil použitý tag za prázdny, bez ohľadu na veľkosť písmen v názve. Použijeme teda regulárny výraz v tvare </?plaintext>. Znak „?“ znamená, že predchádzajúci znak je voliteľný. Prepíšme teda náš servlet tak, aby využíval balíček java.util.regex.*.

ServletFilterRegEx.java:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
public class ServletFilterRegEx extends HttpServlet {
 /* Vytvoríme objekt predstavujúci náš regulárny výraz,
 s ktorým budeme potom porovnávať. Vytvoríme ho tak, aby
 nebol case-sensitive. */

 Pattern pat = Pattern.compile(„</?plaintext>“,
                                  Pattern.CASE_INSENSITIVE);
 /* Vytvoríme zatiaľ prázdnu referenciu na objekt Matcher,
 pomocou ktorého budeme porovnávať a nahrádzať reťazce. */

 Matcher mat = null;
 public void doGet(
          HttpServletRequest req, HttpServletResponse res)
              throws ServletException, IOException {
  String contentType = req.getContentType();
  if (contentType == null) return;
  res.setContentType(contentType);
  PrintWriter out = res.getWriter();
  BufferedReader in = req.getReader();
  try {
   String line = null;
   while ((line = in.readLine()) != null) {
    /* Objektu Matcher predáme odkaz na reťazec, ktorý
    budeme porovnávať s regulárnym výrazom */

    mat = pat.matcher(line);
    /* Metóda matches() vráti true ak predaný reťazec
    spĺňa podmienky regulárneho výrazu */

    if (mat.matches())
     line = mat.replaceAll(„“);
    out.println(line);
   }
  }
  catch(PatternSyntaxException e) {
   log(„Problem with regular expresion: “ + e.getMessage());
  }
 }
 public void doPost(
         HttpServletRequest req, HttpServletResponse res)
              throws ServletException, IOException {
  doGet(req, res);
 }
}

Už na pohľad je tento servlet jednoduchší (ak si odmyslíme komentáre), pretože nám vypadla metóda replace(). Zároveň nám tento spôsob zabezpečil, že algoritmus odstraňovania tagov nie je case-sensitive a nemusíme teda uvažovať so všetkými možnými variantmi. Základom je využívanie objektov Pattern a Matcher. Teraz si uvedieme príklad, využívajúci nástroj PerlTools z knižnice OROMatcher 1.1.

ServletFilterPerl.java:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.oroinc.text.perl.*;
public class ServletFilterPerl extends HttpServlet {
 /* Vytvoríme objekt Perl5Util, potrebný na vytvorenie
 regulárneho výrazu, porovnávanie a nahrádzanie reťazcov */

 Perl5Util perl = new Perl5Util();
 public void doGet(
         HttpServletRequest req, HttpServletResponse res)
                  throws ServletException, IOException {
  String contentType = req.getContentType();
  if (contentType == null) return;
  res.setContentType(contentType);
  PrintWriter out = res.getWriter();
  BufferedReader in = req.getReader();
  try {
   String line = null;
   while ((line = in.readLine()) != null) {
    // porovnáme a ak je výsledok true nahradíme
    if (perl.match(„#</?plaintext>#i“, line))
     line = perl.substitute(„s#</?plaintext>##ig“, line);
    out.println(line);
   }
  }
  catch(MalformedPerl5PatternException e) {
   log(„Problem with regular expresion: “ + e.getMessage());
  }
 }
 public void doPost(
         HttpServletRequest req, HttpServletResponse res)
              throws ServletException, IOException {
  doGet(req, res);
 }
}

Najdôležitejšou časťou tohto servletu je časť, v ktorej porovnávame aktuálny riadok uložený v objekte BufferedReader s regulárnym výrazom v tvare #</?plaintext>#i prostredníctvom metódy match(). V závislosti od jej výsledku sa vykoná alebo nevykoná metóda substitute().

Úlohou prvého riadku je teda vykonať case-insensitive pátranie po reťazcoch odpovedajúcich regulárnemu výrazu. To, že má ísť o case-insensitive pátranie, nastavíme pomocou riadiaceho znaku „i“. Druhý riadok vykoná nahradenie všetkých odpovedajúcich reťazcov za prázdny reťazec. Tento riadok vykoná presne tú istú prácu ako obidva riadky spolu, avšak je efektívnejšie najprv vykonať uvedenú kontrolu. Text medzi prvým a druhým znakom „#“ predstavuje samotný regulárny výraz a text medzi druhým a tretím znakom bude použitý na nahradenie (v tomto prípade je to prázdny reťazec). Riadiaci znak „g“ znamená, že všetky výskyty hľadaného textu budú nahradené. Štandardne sa totiž nahradí len prvý výskyt (jedno nahradenie na jeden riadok). Všetky použité príklady si môžete stiahnuť a vyskúšať sami.

Pre viac informácií ohľadom možností a použitia regulárnych výrazov v spojení s Javou alebo všeobecne, si môžete pozrieť nasledovné odkazy.

Odkazy, zdroje

  • Savarese.Org – hlavná stránka pána Daniela F. Savareseho, na ktorej informuje o projektoch na ktorých pracuje.
  • www.regexp.cz – na tejto stránke si môžete dať otestovať vaše regulárne výrazy pomocou PHP, Perl, JavaScriptu alebo AWK.
  • www.cacas.org – táto stránka ponúka na stiahnutie jeden z nástrojov pre prácu s regulárnymi výrazmi, balíček gnu.regexp.*.
  • www.regular-expressions.info – veľmi dobre obsahovo spracovaný server, obsahujúci veľa užitočných informácií.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět