Java: Tokenizer

Bardzo często zachodzi potrzeba analizowania jakiś danych kawałek po kawałku. W Javie zaimplementowana została obsługa takich specyficznych klas, jak tokenizery. Tokenizery dzielą dane na tzw. tokeny, czyli elementy danych oddzielone spacjami (czyli w przypadku analizowania jakiegoś zdania, tokenizer będzie dzielił zdanie na wyrazy).

W Javie występują 2 rodzaje tokenizerów: StreamTokenizer i StringTokenizer. Ten pierwszy odczytuje dane ze strumienia (np. z pliku czy z połączenia sieciowego), a drugi z podanego stringu. StreamTokenizery działają w bardzo nietypowy sposób. Mianowicie po stworzeniu tokenizera trzeba wywołać metodę nextToken(), która zwróci jedną z trzech interesujących nas wartości:

  • StreamTokenizer.TT_WORD jeśli napotkane dane są stringiem
  • StreamTokenizer.TT_NUMBER jeśli napotkane dane to liczba
  • StreamTokenizer.TT_EOF jeśli napotkany został koniec stringu/strumienia

Tokenizery mają różne funkcje zwracające odczytane dane zależnie od tego co zostało zwrócone przez nextToken(). Jeśli zwrócona została wartość TT_WORD, to do odczytania token znajduje się w zmiennej składowej tokenizera o nazwie sval. Jeśli zwrócona zostąła wartość TT_NUMBER, to wartość jest w nval. Po otrzymaniu wartości TT_EOF należy przerwać czytanie. Teraz może jakiś przykład jak to działa w praktyce. Poniższy przykład czyta dane z pliku i wyświetla je po jednym wyrazie w każdej linii.

int wartosc = 0;
// Wszystkie operacje na plikach muszą być w bloku try ... catch
try{
   // Najpierw trzeba otworzyć jakiś strumień
   FileReader fr = new FileReader("nazwapliku.txt");
   // Teraz otwarcie StreamTokenizera
   StreamTokenizer st = new StreamTokenizer(fr);
   // No i pętla czytająca
   while( (wartosc = st.nextToken()) != StreamTokenizer.TT_EOF ){
      if(wartosc == StreamTokenizer.TT_WORD)
         System.out.println(st.sval);
      else if(wartosc == StreamTokenizer.TT_NUMBER)
         System.out.println(st.nval);
   }
} catch(Exception ex){
   System.out.println("Błąd "+ex);
}

Teraz małe ćwiczenie: napisz program, który z pliku zawierającego słowa i liczby zsumuje wszystkie liczby z tego pliku i wyświetli sumę na ekran. StringTokenizer działa w o wiele prostszy sposób. Klasa ta posiada metodę hasMoreTokens(), która sprawdza czy są jeszcze jakieś tokeny w StringTokenizerze. Warunek ten można wykorzystać w pętli czytającej tokeny. Token jest zwracany przez metodę nextToken(). Odpowiednik powyższego przykładu działający na stringu wygląda tak:

String str = "To jest test";
StringTokenizer st = new StringTokenizer(str);
while(st.hasMoreTokens()){
   System.out.println(st.nextToken());
}

Jak widać, zapis ten jest o wiele krótszy, ale wczytanie pliku do stringu i cięcie stringu na tokeny jest o wiele mniej wydajne niż użycie StreamTokenizera.

Kolejną rzeczą, której warto jest się przyjrzeć, to StringBuffer. StringBuffer jest klasą która ma wspomagać operacje na stringach. Każda trochę bardziej skomplikowana operacja na stringach, taka jak sklejanie stringów, dodawanie czegoś itp powoduje znaczny spadek wydajności, ponieważ tworzonych jest wiele pomocniczych obiektów typu string.

Korzystając ze StringBuffera można zrobić ze stringami dokładnie to samo, co można zrobić ze zwykłymi stringami, tyle że szybciej. StringBuffer tworzy się tak:

StringBuffer sb = new StringBuffer("Początkowy string lub nic");

Do dodania czegoś na końcu StringBuffera służy metoda append(). Jako parametr przyjmuje ona liczbę lub string który ma być dodany. Czyli ma to wyglądać mniej więcej tak:

sb.append("Jakiś inny test");

Do StringBuffera można też wstawiać dane, czyli umieszczać pomiędzy innymi znakami gdzieś w środku.

StringBuffer sb = new StringBuffer("To jest test");
sb.insert(7, " bardzo fajny");
// W tym momencie StringBuffer zawiera ciąg "To jest bardzo fajny test"

Tylko że po co nam StringBuffer, skoro funkcje jako parametry wymagają typu String? StringBuffer byłby całkowicie nieprzydatny, gdyby nie było możliwości konwersji do stringu. String zawierający zawartość StringBuffera zwraca metoda toString(). Czyli wyświetlenie na ekran zawartości StringBuffera wygląda mniej więcej tak:

System.out.println(sb.toString());

StringBuffer ma jeszcze kilka ciekawych możliwości, takich jak wycinanie substringów czy sprawdzanie długości zawartości, ale to wszystko można znaleźć w dokumentacji.

Autor: leafnode

Architekt oprogramowania webowego, programista, analityk bezpieczeństwa serwisów internetowych, speaker, konsultant. Potrzebujesz pomocy? Skontaktuj się ze mną!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *