Uso de la clase StringTokenizer

Uso de los metodos basicos de la clase StringTokenizer; nextToken(),hasMoreTokens(),countTokens(). En esta ocasion voi a mostrar el uso de tres metodos de la clase StringTokenizer. Esta clase permite obtener por separado las palabras (tokens) que conforman una cadena, para obtener las palabras separadas se puede especificar el separador que hay entre estas, por defecto sino se indica Java asume que el separador es el espacio. Esta clase tiene varios constructores pero a mi principalmente me parecen interesantes dos:

El primero recibe como entrada una cadena, la cual es obviamente la cadena que contiene las palabras que se quieren obtener por separado. En este ejemplo voi a suponer que se desea obtener los tokens de una cadena la cual es una de las 4 operaciones SQL (INSERT, DELETE, UPDATE o SELECT). Ejemplos del uso de este constructor:

//La clase se encuentra en el paquete java.util
import java.util.StringTokenizer;
//Ejemplo del uso del contructor1, donde java asume el espacio como separador
StringTokenizer token1 = new StringTokenizer("SELECT nombre,apellidos,edad FROM alumnos");
StringTokenizer token2 = new StringTokenizer("EJEMPLO DE UNA CADENA PARA EFECTOS DE PRUEBA");
StringTokenizer token3 = new StringTokenizer("Instituto Tecnologico de Zacatepec");

Las cadenas anteriores tienen 4,8,4 tokens respectivamente.
El segundo constructor recibe como entrada una cadena y un caracter separador (en realidad puede ser otra cadena). Ejemplos:

//La clase se encuentra en el paquete java.util
import java.util.StringTokenizer;
//Ejemplo del uso del contructor2, donde se especifica explicitamente el separador de los tokens
StringTokenizer token1 = new StringTokenizer("04-04-2011","-");
StringTokenizer token2 = new StringTokenizer("gonzasilve:x:502:502:   Gonzalo Silverio:/home/gonzasilve:/bin/bash",":");

Ahora las cadenas anteriores tienen 3 y 7 tokens respectivamente. Ojo, observa que el espacio ya no es el separador en ninguna de las cadenas anteriores, por eso tienen ese numero de tokens.

Bueno ahora vamos con los metodos, los describo brevemente a continuacion:

nextToken()
Devuelve una subcadena (String) que es el siguiente token de la cadena. Imaginemos que cada vez que se llama a este metodo hay un apuntador y el cual se posiciona en el siguiente token. Observa la siguiente imagen

Uso del metodo nextToken() de la clase StringTokenizer

Uso del metodo nextToken() de la clase StringTokenizer

La primera vez que se llama a este metodo, el apuntador esta posicionado en la primera palabra por lo tanto el metodo devolveria el token «INGENIERIA» y el apuntador se mueve al siguiente token.
La segunda vez que se llama a este metodo, el apuntador esta posicionado en la segunda palabra por lo tanto el metodo devolveria el token «EN» y el apuntador se mueve al siguiente token.
La tercera vez que se llama a este metodo, el apuntador esta posicionado en la tercera palabra por lo tanto el metodo devolveria el token «SISTEMAS» y el apuntador se mueve al siguiente token.
La cuarta vez que se llama a este metodo, el apuntador esta posicionado en la cuarta palabra por lo tanto el metodo devolveria el token «COMPUTACIONALES» y el apuntador se mueve al siguiente token.

Ahora observa la siguiente imagen donde se observa la posicion donde quedo el apuntador despues de llamar por cuarta vez al metodo nextToken():

Uso del metodo nextToken() de la clase StringTokenizer

Uso del metodo nextToken() de la clase StringTokenizer


Si se llama por quinta vez al metodo nextToken() ocurrira una excepcion porque ya no hay un siguiente token.

hasMoreTokens()
Devuelve un valor de tipo boolean (true o false) que indica si hay (true) un siguiente token. Sino hay un siguiente token devuelve false.

countTokens()
Devuelve un entero (int) que es el numero de tokens de la cadena. Bueno a continuacion muestro un ejemplo mas elaborado que muestra el uso de estos tres metodos. No explico el codigo debido a que ya puse comentarios.

/*
 *    Instituto Tecnologico de Zacatepec
 *	Descripcion Ejemplo1 de uso de la clase StringTokenizer
 * author Gonzalo Silverio    gonzasilve@hotmail.com
 * version 1.00 
 * Archivo: EjemploTokenizer1.java
 */

import java.util.StringTokenizer;

public class EjemploTokenizer1
{
	StringTokenizer st;
	//Recine como entrada la cadena de la cual se quieren sacar su tokens
	public EjemploTokenizer1(String cadena)
	{
		st = new StringTokenizer(cadena);
	}
    
    //Muestra en la consola los tokens de la cadena
	public void mostrarTokens()
	{
		//Mientras haya mas tokens
		//hasMoreTokens() devuelve true si hay un siguiente token o  false si ya no hay
		while (st.hasMoreTokens())
		{
		    System.out.println(st.nextToken());
		}		
	}

    //Devuelve un String con el siguiente token
	public String siguienteToken()
	{
		//El metodo nextToken devuelve un string con el siguiente token
		return st.nextToken();
	}
	   
   //Devuelve el numero de tokens que tiene la cadena
	public int contarTokens()
	{
		return st.countTokens();
	}
	
	//hasMoreTokens() devuelve true si hay un siguiente token o  false si ya no hay
	public boolean hayOtroToken()
	{
		return st.hasMoreTokens();		
	}
	
    public static void main(String gonza[])
    {
    	//Reinicializar objeto con cadena SQL
    	EjemploTokenizer1 objeto1 = new EjemploTokenizer1("SELECT nombre,apellidos,edad FROM alumnos");
    	System.out.println("tokens en esta cadena: "+ objeto1.contarTokens () );
    	System.out.println(objeto1.siguienteToken() );	//Muestra SELECT
    	System.out.println(objeto1.siguienteToken() ); 	//Muestra los campos seleccionados
    	System.out.println(objeto1.siguienteToken() );	//Muestra FROM
    	System.out.println(objeto1.siguienteToken() );	//Muestra el nombre de la tabla
    	System.out.println("hay otro token: "+ objeto1.hayOtroToken () );	//Muestra si hay otro token
    	
    	//Se reinicializa el objeto con otra cadena SQL
    	objeto1 = new EjemploTokenizer1("INSERT  INTO S VALUES('s1','intel','cuerna',245)");
    	System.out.println("\ntokens en esta cadena: "+ objeto1.contarTokens () );
    	objeto1.mostrarTokens();
    	
    	
    }
    
}

Ahora una imagen de la compilacion y ejecucion de este ejemplo:

Compilacion  y ejecucion del ejemplo StringTokenizer

Compilacion y ejecucion del ejemplo StringTokenizer

Bueno, eso es todo y ya sabes si quieres el .java basta con pedirlo. Hasta pronto y gracias por tu visita.

Analizador lexico para expresiones SQL

En esta ocasion dejo un ejemplo de un analizador lexico de expresiones SQL, es una clase. En el constructor de esta clase recibe como entrada una cadena SQL y devuelve cada vez que se le llama a su metodo obtener_token() el siguiente token de la cadena SQL. podria usarse como punto de entrada para el analizador sintactico de cualquiera de las 4 operaciones SQL; INSERT, DELETE, UPDATE o SELECT. El codigo de la clase es el siguiente:


//		Instituto Tecnologico de Zacatepec
//  En el constructor de esta clase recibe como entrada una cadena SQL y devuelve cada vez que se 
//  se llama a su metodo  obtenerToken() devuelve el siguiente token de la cadena SQL.
//  Author: Gonzalo Silverio   gonzasilve@hotmail.com

public class Tokens
{
	//### Atributos de la clase ####
   private StringBuffer tok;    //Para guardar el lexema del token
   private char car;          	//Almacena un caracter de la expresionSQL
   public String token;    		//Para que el token este disponible como String y no como StringBuffer
   public int intTipoToken;    		//Guarda el tipo de token en forma numerica
   public String strTipoToken = new String();		//Guarda el Tipo de token en forma de cadena 
	public String strInstruccionSQL;		//Guarda la cadena SQL que se esta analizando

   //Tipos de token en formato numerico
   public final int 	FIN_DE_ARCHIVO  	=  -1;
   public final int 	NUMERO_ENTERO   	=  2;
   public final int 	DELIMITADOR     	=  3;
   public final int  SALTO_DE_LINEA  	=  4;
   public final int  NINGUNO         	=  5;
   public final int  IDENTIFICADOR   	=  6;
   public final int  PALABRA_CLAVE   	=  7;
   public final int  CADENA          	=  8;
   public final int  PARENTESIS      	=  9;
   public final int 	NUMERO_DECIMAL  	=  10;
   public final int 	ASTERISCO     		=  11;
   public final int 	ERROR   				=  12;
   public final int 	OPERADOR				=	13;

	//indice  para recorrer caracter por caracter la expresionSQL
   private int indexExpSQL;     

   //Arreglo de palabras clave
   private static String palabras_clave[] = {"update","insert","delete","select","set","from","where","and","or","not","into","values","group","having","by","between"};

    
    //             TIPOS DE TOKEN     
    //     Tipos de token que se reconocen...
    // 
    //    --Tipo de token--      --Incluye--
    //    Delimitador          Signos de puntuacion punto (.) y la coma (,)
    //    Palabras clave       Palabras clave
    //    Cadena               Cadenas entre comillas simples
    //    Identificador        Nombre de variable (nombres de tablas, campos, etc)  o nombre de funcion (de agregacion)
    //    Numero_entero        Constante numerica entera
    //    Numero_decimal       Constante numerica decimal
    //    Operador			 	 Operadores aritmeticos y relacionales
        

	 Tokens(String cadSQLAParsear)
	 {
	     strInstruccionSQL = cadSQLAParsear+"; ";		//Agregamos punto y coma ;
	     //Inicializar con por default los atributos
	     car = ' ';
	     indexExpSQL = 0;      //Iniciar analisis en el primer caracter de la cadena
	     tok  =  new StringBuffer();
		  token = "";
	     leerCaracter();
	 }
	//Constructor vacio
	Tokens()
	{

	}

	//Metodo que lee el siguiente token de la cadena SQL
    public void obtenerToken()
    {
    	tok = new StringBuffer();		// Reinicializar variable para leer otro token
    	token = new String();		// Reinicializar variable para leer otro token  
    	intTipoToken = NINGUNO;                 //Por defecto no es ningun tipo de token
    	strTipoToken = "ninguno";
		token = "";

        // SALTARSE ESPACIOS EN BLANCO Y TABULADORES  
        for(;; leerCaracter())
         {
            if( (char)car == ' ' || (char)car == '\t' )
               continue;
            else
               break;
         }
       
			//Comprobar si es un operador relacional
        switch(car)
        {
           case '<':
              if(leerCaracter('>'))
              {
                 tok.append('<');
                 tok.append('>');
              }
              else if( leerCaracter('=') )
              {
                 tok.append('<');
                 tok.append('=');
              }
              else
                 tok.append('<');
              break;

           case '>':
              if(leerCaracter('='))
              {
                 tok.append('>');
                 tok.append('=');
              }
              else
                 tok.append('>');
              break;

           case '!':						//Operador No
              if(leerCaracter('='))
              {
                 tok.append('!');
                 tok.append('=');
              }
              else
                 tok.append('!');
              break;

        }      //Fin comprobbar si es operador relacional
    
		 //Si la variable tiene algo se leyo un operador relacional
       if(tok.length()>0)
       {
          intTipoToken = OPERADOR;
          strTipoToken = "operador";
			 token = tok.toString().toLowerCase();
          leerCaracter();
          return;
       }

			//Comprobar si es un operador aritmetico
       String delims = new String("+-=");
       int d = delims.indexOf(car);
       if( d >= 0 )
       {
          tok.append(car);
          intTipoToken = OPERADOR;           //Es un OPERADOR (aritmetico)
          strTipoToken = "operador";
			 token = tok.toString().toLowerCase();
          leerCaracter();
          return;
       }

			//comprobar si es un delimitador
       delims = new String(",.;");
       d = delims.indexOf(car);
       if( d >= 0 )
       {
          tok.append(car);
          intTipoToken = DELIMITADOR;           //Es un delimitador punto o coma
          strTipoToken = "delimitador";
			 token = tok.toString().toLowerCase();
          leerCaracter();
          return;
       }

			//Comprobar si son parentesis
       String parentesis = new String("()");
       int p = parentesis.indexOf(car);
       if( p >= 0 )
       {
          tok.append(car);
          intTipoToken = PARENTESIS;           //Es un parentesis
          strTipoToken= "parentesis";
			 token = tok.toString().toLowerCase();
          leerCaracter();
          return;
       }

		// Comprobar si es una cadena
		// La cadena, se guarda sin comillas 
       if( (char)car == '\'' )
       {
           leerCaracter(); 	// Saltarse la comilla de apertura 
          while((char)car != '\'')
          {
              tok.append(car);
              leerCaracter();
          }
          leerCaracter();			// Saltarse la comilla de cierre

          intTipoToken = CADENA;
          strTipoToken= "char";
			 token = tok.toString().toLowerCase();
          return;
       }
       
       //Comprobar Si es una identificador de usuario o una palabra clave de SQL
       if(Character.isLetter((char)car))
       {
          do
          {
             tok.append(car);
             leerCaracter();
          } while(Character.isLetterOrDigit( (char)car) || (char)car == '-' || (char)car == '_' || (char)car == '.' );

           if( contarPuntos(tok)>1 )
           {
              mensaje_error("El identificador \'" +tok.toString()+ "\' tiene demasiados puntos");
              intTipoToken = ERROR;
              strTipoToken = "error";
				  token = tok.toString().toLowerCase();
              return;
           }
           
             //comprobar si es una palabra clave, sino, entonces es un identificador
          if( buscar_keyword( tok.toString().toLowerCase() ) )
          {
             intTipoToken = PALABRA_CLAVE;
             strTipoToken = "keyword";
				 token = tok.toString().toLowerCase();
          }
          else
          {
             intTipoToken = IDENTIFICADOR;
             strTipoToken = "identificador";
				 token = tok.toString().toLowerCase();
          }
        return;      
   	}
       
			//Si es un numero (entero o decimal)
	   if(Character.isDigit((char)car))
	   {      
	      do
	      {
	         tok.append(car);
	         leerCaracter();
	      } while(Character.isDigit( (char)car) || (char)car == '.' );
	      
				//Comprobar si es un entero
	      if(contarPuntos(tok) == 0 )
	      {
	          if(esEntero(tok.toString()))
	          {
	          	intTipoToken = NUMERO_ENTERO;
	          	strTipoToken = "int";
					token = tok.toString().toLowerCase();
	          }	              
	      }		//Comprobar si es un decimal
	      else if(contarPuntos(tok) == 1)
	      {
	          if(esDecimal(tok.toString()))
	          {
	          	intTipoToken = NUMERO_DECIMAL;
	          	strTipoToken = "decimal";
					token = tok.toString().toLowerCase();
	          }	              
	          else
	          {
	              //Informar al usuario del error de sintaxis en el decimal
	              mensaje_error("El token \'" +tok.toString()+ "\' parece un decimal pero tiene un error de sintaxis");
	              intTipoToken = ERROR;
	              strTipoToken = "error";
					  token = tok.toString().toLowerCase();
	          }
	      }
	      else if(contarPuntos(tok) > 1 )             //Si tiene mas de un punto
	      {
	          //Informar a usuario que un decimal no puede tener mas de 1 caracter punto
	          mensaje_error("El token \'" +tok.toString()+ "\' tiene mas de un punto");
	          intTipoToken = ERROR;
	          strTipoToken = "error";
				 token = tok.toString().toLowerCase();
	      }
	      return;
	   }          //Fin de validar si es un numero
       
			//Comprobar si es asterisco
	   if((char)car == '*' )
	   {
	      tok.append(car);
	      intTipoToken = ASTERISCO;
	      strTipoToken = "asterisco";
			token = tok.toString().toLowerCase();
	      leerCaracter();
	      return;
	   }
  }          //Fin del metodo obtenerToken

   //Devuelve true si en una cadena que llega todos son numeros, false en caso contrario
    public boolean esEntero(String cad)
    {
        for(int i = 0; i<cad.length(); i++)
            if( !Character.isDigit(cad.charAt(i)) )
                return false;

        return true;
    }

    //Devuelve true si la cadena que llega tiene la sintaxis de un decimal
    public boolean esDecimal(String cad)
    {
        boolean hayPunto=false;
        StringBuffer parteEntera = new StringBuffer();
        StringBuffer parteDecimal = new StringBuffer();
        int i=0, posicionDelPunto;

        for( i=0;i<cad.length(); i++ )
            if ( cad.charAt(i) == '.')                          //Detectar si hay un punto decimal en la cadena
                hayPunto=true;
        if(hayPunto)                                            //Si hay punto guardar la posicion donde se encuentra el carater punto
            posicionDelPunto=cad.indexOf('.');                  //(si la cadena tiene varios puntos, detecta donde esta el primero).
        else
            return false;                                       //Si no hay punto; no es decimal

        if( posicionDelPunto == 0 || posicionDelPunto == cad.length()-1  )    //Si el punto esta al principio o al final no es un decimal
            return false;

        for( i=0;i<posicionDelPunto; i++ )
            parteEntera.append(cad.charAt(i)) ;                 //Guardar la parte entera en una variable

        for(i = 0; i<parteEntera.length(); i++)
            if( ! Character.isDigit(parteEntera.charAt(i)) )    //Si alguno de los caracteres de la parte entera
                return false;									//no son digitos no es decimal

        for( i=posicionDelPunto+1;i<cad.length(); i++ )
            parteDecimal.append(cad.charAt(i));                 //Guardar la parte decimal en una variable

        for(i = 0; i<parteDecimal.length(); i++)
            if( ! Character.isDigit(parteDecimal.charAt(i)) )   //Si alguno de los caracteres de la parte decimal no es un digito no es decimal
                return false;                                   //Incluye el caso en el que la cadena tenga dos o mas puntos

        return true;                                            //Si paso todas las pruebas anterior, la cadena es un Numero decimal
    }
    
   //Lee un caracter de la cadenaSQL
   void leerCaracter()  throws StringIndexOutOfBoundsException
   {     
      
            car = strInstruccionSQL.charAt(indexExpSQL);		/* leer siguiente caracter */
                  
            //Verificar si se a sobrepasado el indice maximo de caracteres en la expresion SQL
            if( indexExpSQL < strInstruccionSQL.length() )
               indexExpSQL++;
            else
               indexExpSQL = strInstruccionSQL.length()-1;
   }

	// lee un caracter x adelantado de la cadena SQL, el caracter que llega lo compara con
	//el caracter leido y si son iguales devuelve true, si son diferentes devuelve false
   boolean  leerCaracter(char c)
   {
      leerCaracter();
      if( (char)car != (char)c )
      {
          indexExpSQL--;
          return false;
      }         
      
      car = ' ';
      return true;
             
   }

   //Cuenta el numero de caracteres punto que tiene una cadena especificada
   public int contarPuntos(StringBuffer cad)
   {
       int contador = 0;
       for(int i=0;i<cad.length();i++)
       {
           if(cad.charAt(i)=='.')
               contador++;
       }
       return contador;
   }
   
   //Devuelve true si la cadena que llega es una palabra clave, false en caso contrario
   //busca en la tabla de palabras clave la cadena que llega
   static boolean buscar_keyword(String identificador)
   {
      for(int i = 0;i< palabras_clave.length;i++)
      {
         if( palabras_clave[i].equals(identificador) )
               return true;
      }
      return false;
   }
   
	// Muestra un msg de error en el area de msgs del parser 
   public void mensaje_error(String strMsg)
   {
       System.out.print("\n" + strMsg );
   }   

}		//Fin de la clase Tokens.java

Como veo que a mas de uno le a resultado interesante mi clase a continuacion doy una breve explicacion de la clase Tokens. La Clase Tokens como se puede observar, tiene varios atributos pero solo 4 son publicos, los cuales son token,intTipoToken,strTipoToken,strInstruccionSQL de estos solo los primeros 3 son de utilidad para usarse para hacer un analizador sintacto de alguna sentencia SQL. Ademas de estos 4 atributos hay un unico metodo llamado obtenerToken() que no devuelve ningun valor (void). La clase UML se ve mas o menos asi si omitimos los demas atributos…
Clase Tokens

Cada vez que se llama al metodo las 3 variables que mencione arriba quedan con los valores que nos interesan, por ejemplo, suponiendo que la operacion SQL que se le pasa a esta clase es:

INSERT INTO alumnos VALUES('07680654','Carlos','Zarate', 23,78.0)

Para pasarle esta cadena SQL se debe crear una instancia de la clase, algo asi:

Tokens pruebaTokens = new Tokens("INSERT INTO alumnos VALUES('07680654','Carlos','Zarate', 23,78.0)");

y al llamar al metodo obtenerToken() del objeto pruebaTokens, las tres variables quedarian con los valores:

Variable Valor
token «insert»
intTipoToken PALABRA_CLAVE
strTipoToken «keyword»

si se llama al metodo por segunda vez, ahora los valores de estas variables son:

Variable Valor
token «into»
intTipoToken PALABRA_CLAVE
strTipoToken «keyword»

Si se llama al metodo por tercera vez, ahora los valores de estas variables son:

Variable Valor
token «alumnos»
intTipoToken IDENTIFICADOR
strTipoToken «identificador»

Las constantes sirven para tener una representacion en forma numerica de cada uno de los tokens, los puse por si me servian de algo mas adelante, y vaya que si me sirvieron. Ademas, como se observa hay un arreglo que contiene las palabras clave de SQL, si me falto alguna o se desean agregar mas (por ej. para ampliar las de SQL) pues ahi es donde se agregarian. Aca adelante un ejemplo del uso de esta clase…


//  	Instituto Tecnologico de Zacatepec, Morelos, Mexico
// Prueba de la clase Tokens
// Author:  Gonzalo Silverio   gonzasilve@gmail.com

public class PruebaTokens
{
   //Tipo de token que puede haber en la cadena
   public final int 	FIN_DE_ARCHIVO  =  -1;
   public final int 	NUMERO_ENTERO          =   2;
   public final int 	DELIMITADOR     =   3;
   public final int   	SALTO_DE_LINEA  =   4;
   public final int   	NINGUNO         =   5;
   public final int   	IDENTIFICADOR   =   6;
   public final int   	PALABRA_CLAVE   =   7;
   public final int   	CADENA          =   8;
   public final int   	PARENTESIS      =  9;
   public final int 	NUMERO_DECIMAL   =   10;
   public final int 	ASTERISCO   =   	11;
   public final int 	ERROR   =   	12;
   
   //El constructor espera recibir una cadena sql de la cual se quieren extraer sus tokens
   private void analizarCadenaSQL(String cadSQL)
   {
      Tokens tokens = new Tokens(cadSQL);

//      System.out.println("\n\n");
      //Este bucle imprime los tokens que se van leyendo y el tipo de token que se leyo de la cadena SQL
      tokens.obtenerToken();		//Obtener el primer token de la cadena SQL
      while( ! tokens.token.equals(";") )
      {
         switch(tokens.intTipoToken)
         {
            case NUMERO_ENTERO:
               System.out.println(tokens.token+" \t\t\tNUMERO_ENTERO");
               break;
            case NUMERO_DECIMAL:
               System.out.println(tokens.token+" \t\t\tNUMERO_DECIMAL");
               break;
            case PALABRA_CLAVE:
               System.out.println(tokens.token+" \t\t\tPALABRA_CLAVE");
               break;
            case IDENTIFICADOR:
               System.out.println(tokens.token+" \t\t\tIDENTIFICADOR");
               break;
            case CADENA:
               System.out.println(tokens.token+" \t\t\tCADENA");
               break;
            case PARENTESIS:
               System.out.println(tokens.token+" \t\t\tPARENTESIS");
               break;
            case DELIMITADOR:
               System.out.println(tokens.token+" \t\t\tDELIMITADOR");
               break;
            case ASTERISCO:
               System.out.println(tokens.token+" \t\t\tASTERISCO");
               break;
            case ERROR:
               System.out.println(tokens.token + " \t\t\t<-- ERROR !");
               break;
         }
			tokens.obtenerToken();
		}
   }

   public static void main(String args[])
   {
      PruebaTokens prueba1 = new PruebaTokens();
      prueba1.analizarCadenaSQL("insert into s values('s1','ford','mty', 23,58.42)");
   }
}

y el resultado de la ejecucion es la siguiente:

gonzasilve

Si les aparece algun error al compilar o desean el fuente, les puedo pasar el .java, basta con dejar un comentario con su e-mail

hasta la proxima, espero comentarios….
}