Po prostu dla własnych celów, próbuję zbudować tokenizera w Javie, gdzie mogę zdefiniować regularną gramatykę i na tej podstawie tokenizować dane wejściowe. Klasa StringTokenizer jest przestarzała i znalazłem kilka funkcji w skanerze, które wskazują na to, co chcę zrobić, ale bez powodzenia. Ktoś wie dobrze o tym wszystkim?Jak mogę tokenizować dane wejściowe przy użyciu klasy skanera Java i wyrażeń regularnych?
Odpowiedz
Nazwa "Skaner" jest nieco myląca, ponieważ słowo to często jest używane w znaczeniu analizatora leksykalnego, a to nie jest to, do czego służy Skaner. Wszystko to jest substytutem funkcji scanf()
, którą można znaleźć w C, Perl, i innych. Podobnie jak StringTokenizer i split()
, jest przeznaczony do skanowania z wyprzedzeniem, dopóki nie znajdzie dopasowania dla danego wzorca, a cokolwiek przeskoczyło po drodze, jest zwracane jako token.
Z kolei analizator leksykalny musi badać i klasyfikować każdą postać, nawet jeśli jest to tylko decyzja, czy można ją bezpiecznie zignorować. Oznacza to, że po każdym dopasowaniu może zastosować kilka wzorów, dopóki nie znajdzie takiego, który pasuje do począwszy od tego punktu. W przeciwnym razie może znaleźć sekwencję "//" i pomyśleć, że odnalazł początek komentarza, kiedy jest naprawdę wewnątrz literału i nie zauważył początkowego cudzysłowu.
Oczywiście jest to znacznie bardziej skomplikowane, ale ja po prostu ilustruję, dlaczego wbudowane narzędzia, takie jak StringTokenizer, split()
i skaner, nie nadają się do tego rodzaju zadań. Możliwe jest jednak użycie klas regex Java w ograniczonej formie analizy leksykalnej. W rzeczywistości dodanie klasy Scanner znacznie ułatwiło korzystanie z nowego interfejsu API Matchera, który został dodany w celu jego obsługi, tj. Regionów i metody usePattern()
. Oto przykład prymitywnego skanera zbudowanego na klasach regex Java.
import java.util.*;
import java.util.regex.*;
public class RETokenizer
{
static List<Token> tokenize(String source, List<Rule> rules)
{
List<Token> tokens = new ArrayList<Token>();
int pos = 0;
final int end = source.length();
Matcher m = Pattern.compile("dummy").matcher(source);
m.useTransparentBounds(true).useAnchoringBounds(false);
while (pos < end)
{
m.region(pos, end);
for (Rule r : rules)
{
if (m.usePattern(r.pattern).lookingAt())
{
tokens.add(new Token(r.name, m.start(), m.end()));
pos = m.end();
break;
}
}
pos++; // bump-along, in case no rule matched
}
return tokens;
}
static class Rule
{
final String name;
final Pattern pattern;
Rule(String name, String regex)
{
this.name = name;
pattern = Pattern.compile(regex);
}
}
static class Token
{
final String name;
final int startPos;
final int endPos;
Token(String name, int startPos, int endPos)
{
this.name = name;
this.startPos = startPos;
this.endPos = endPos;
}
@Override
public String toString()
{
return String.format("Token [%2d, %2d, %s]", startPos, endPos, name);
}
}
public static void main(String[] args) throws Exception
{
List<Rule> rules = new ArrayList<Rule>();
rules.add(new Rule("WORD", "[A-Za-z]+"));
rules.add(new Rule("QUOTED", "\"[^\"]*+\""));
rules.add(new Rule("COMMENT", "//.*"));
rules.add(new Rule("WHITESPACE", "\\s+"));
String str = "foo //in \"comment\"\nbar \"no //comment\" end";
List<Token> result = RETokenizer.tokenize(str, rules);
for (Token t : result)
{
System.out.println(t);
}
}
}
To, nawiasem mówiąc, jest to jedyny dobry użytek, jaki kiedykolwiek znaleziono metody lookingAt()
. : D
Jeśli dobrze rozumiem twoje pytanie, oto dwie przykładowe metody do tokenizacji ciągu znaków. Nie potrzebujesz nawet klasy Scanner, tylko jeśli chcesz wstępnie rzucić tokeny lub iterować po nich bardziej sofistycznie niż przy użyciu tablicy. Jeśli tablica jest wystarczająca, po prostu użyj String.split() jak podano poniżej.
Podaj więcej wymagań, aby umożliwić bardziej precyzyjne odpowiedzi.
Tak, powinienem był opracować więcej. Jest to pomocne przy dzieleniu ciągu ** wokół ** dopasowań do wyrażenia regularnego, ale nie w celu znalezienia tokenów, które faktycznie pasują do wyrażenia regularnego. – eplawless
Jeśli dotyczy to prostego projektu (aby dowiedzieć się, jak to wszystko działa), przejdź do tego, co powiedział Balint Pato.
Jeśli dotyczy to większego projektu, należy zamiast tego użyć generatora skanerów, takiego jak JFlex. Nieco bardziej skomplikowane, ale szybsze i mocniejsze.
Chciałbym również bardzo polecić JFlex do wszystkiego, co nie jest trywialne. Zapisywanie specyfikacji skanera wymaga pewnej praktyki, ale JFlex ma dobre pliki startowe i jest świetną umiejętnością do zdobycia. – Josh
Większość odpowiedzi tutaj jest już znakomita, ale byłbym niedbały, gdybym nie wskazał ANTLR. Stworzyłem całe kompilatory wokół tego doskonałego narzędzia. Wersja 3 ma niesamowite funkcje i polecam ją dla każdego projektu, który wymaga od ciebie parsowania danych wejściowych w oparciu o dobrze zdefiniowaną gramatykę.
- 1. daty mecz przy użyciu Python wyrażeń regularnych
- 2. Sed przy użyciu rozszerzonych grup wyrażeń regularnych i przechwytywania
- 3. Odczytywanie pliku przy użyciu skanera Java
- 4. Java skanera klasy czytania ciągi
- 5. Jak uzyskać numer wiersza przy użyciu skanera
- 6. Jak zamaskować dane wejściowe za pomocą wyrażeń regularnych? albo 1112223333 lub 1112223333444
- 7. Java: Jak uzyskać dane wejściowe z System.console()
- 8. Java za pomocą klawisza Enter przy użyciu skanera wciśniętego
- 9. Jak dołączyć - i "w tych wyrażeń regularnych?
- 10. Łączenie wyrażeń regularnych w JavaScript
- 11. Limit grupy przechwytywania-wyrażeń regularnych wyrażeń regularnych SQL?
- 12. Łączenie dowolnych wyrażeń regularnych
- 13. Swift format wyrażeń regularnych?
- 14. Dopasuj tekst wielowierszowy za pomocą wyrażeń regularnych
- 15. Wyodrębnianie danych Java za pomocą wyrażeń regularnych między znacznikami href
- 16. Składnia wyrażeń regularnych Haskella
- 17. Scalanie dwóch wyrażeń regularnych
- 18. Łączenie wyrażeń regularnych
- 19. Jak znaleźć słowo za pomocą dot przy użyciu wyrażeń regularnych w Javie?
- 20. jak dopasować koniec ciągów za pomocą wyrażeń regularnych w java
- 21. Bezpieczeństwo wyrażeń regularnych
- 22. Ungreedy wyrażeń regularnych
- 23. Wyszukiwarka wyrażeń regularnych
- 24. Generatywne wyrażeń regularnych
- 25. Wyodrębnij dane między znakami za pomocą wyrażeń regularnych?
- 26. wyrażeń regularnych brakuje niektórych liter
- 27. Jak używać wyrażeń regularnych z Mongodb ODM Doctrine?
- 28. Java: Matcher.find przy użyciu wysokiego cpu
- 29. Jak używać wyrażeń regularnych w Jinja2?
- 30. Grupy wejściowe większe niż dane wejściowe w Bootstrap 3 przy użyciu kontenera Jumbotron
Twoja pętla końcowa musi zwiększać liczbę pozycji przed pętlą "dla reguł", na wypadek gdyby reguły nie były prawidłowe?W przeciwnym razie ładny przykład i podziękowania za sugestię lookingAt(). –
Dobry połów. Tak, powinien istnieć "pos ++" zaraz po pętli for. Może to być przykład bez blasku bez sprawdzania błędów, ale przynajmniej powinienem upewnić się, że nie ma żadnych potencjalnych nieskończonych pętli. –
Bardzo podoba mi się to podejście i użyłem tego jako przykładu dla mojego własnego kodu właśnie wczoraj. Zauważyłem jednak, że kolejność list Reguł może wpływać na wyniki. W moim rozwiązaniu próbuję dopasować się do wszystkich reguł, zamiast łamać po pierwszym meczu. Następnie wybieram najdłuższy mecz. –