scanner - ¿Cómo puedo tokenizar la entrada usando la clase de escáner de Java y las expresiones regulares?
expresiones regulares java rango (4)
Si entiendo bien su pregunta, aquí hay dos métodos de ejemplo para tokenizar una cadena. Ni siquiera necesita la clase de escáner, solo si desea predefinir los tokens, o iterar a través de ellos de forma más sofisticada que utilizando una matriz. Si una matriz es suficiente, simplemente use String.split () como se indica a continuación.
Proporcione más requisitos para permitir respuestas más precisas.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
String textToTokenize = "This is a text that will be tokenized. I will use 1-2 methods.";
Scanner scanner = new Scanner(textToTokenize);
scanner.useDelimiter("i.");
while (scanner.hasNext()){
System.out.println(scanner.next());
}
System.out.println(" **************** ");
String[] sSplit = textToTokenize.split("i.");
for (String token: sSplit){
System.out.println(token);
}
}
}
Solo para mis propios fines, estoy tratando de construir un tokenizador en Java donde pueda definir una gramática regular y hacer que la entrada de tokenice en función de eso. La clase StringTokenizer está en desuso, y he encontrado un par de funciones en Scanner que insinúan lo que quiero hacer, pero todavía no tengo suerte. Alguien sabe una buena manera de hacer esto?
El nombre "Escáner" es un poco engañoso, porque la palabra a menudo se usa para referirse a un analizador léxico, y no es para lo que es Scanner. Todo lo que es es un sustituto de la función scanf()
que se encuentra en C, Perl, et al . Al igual que StringTokenizer y split()
, está diseñado para escanear hacia adelante hasta que encuentre una coincidencia para un patrón dado, y todo lo que omitió en el camino se devuelve como un token.
Un analizador léxico, por otro lado, tiene que examinar y clasificar cada personaje, incluso si solo es para decidir si puede ignorarlos de manera segura. Eso significa que, después de cada partida, puede aplicar varios patrones hasta que encuentre uno que coincida comenzando en ese punto . De lo contrario, puede encontrar la secuencia "//" y pensar que se encontró el comienzo de un comentario, cuando está realmente dentro de un literal de cadena y simplemente no notó la comilla de apertura.
En realidad, es mucho más complicado que eso, por supuesto, pero solo estoy ilustrando por qué las herramientas integradas como StringTokenizer, split()
y Scanner no son adecuadas para este tipo de tarea. Sin embargo, es posible utilizar las clases de expresiones regulares de Java para una forma limitada de análisis léxico. De hecho, la adición de la clase Scanner lo hizo mucho más fácil, debido a la nueva API de Matcher que se agregó para usePattern()
, es decir, regiones y el método usePattern()
. Aquí hay un ejemplo de un escáner rudimentario construido sobre las clases de expresiones regulares de 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);
}
}
}
Por cierto, este es el único buen uso que he encontrado para el método lookingAt()
. :RE
La mayoría de las respuestas aquí son excelentes, pero sería negligente si no señalara ANTLR . Creé compiladores completos en torno a esta excelente herramienta. La versión 3 tiene algunas características increíbles y la recomendaría para cualquier proyecto que requiriera que analizara la entrada en base a una gramática bien definida.