2009-10-01 21 views
9

Quiero tokenize una cadena como estaTokenize una cadena con un espacio en Java

String line = "a=b c='123 456' d=777 e='uij yyy'"; 

no puedo dividir basada gusta este

String [] words = line.split(" "); 

Alguna idea de cómo puedo dividir de modo que consiga fichas como

a=b 
c='123 456' 
d=777 
e='uij yyy'; 
+0

¿No podría simplemente usar una expresión regular para dividir por espacios a menos que esté dentro de una cita (no es que yo sepa la expresión regular, pero estoy bastante seguro de que puede hacerlo). – mk12

+0

Su código funciona perfectamente aquí usando jdk 1.6.0_13 –

+0

@LePad arriba del código dará salida a * [a = b, c = '123, 456', d = 777, e = 'uij, yyy'] * –

Respuesta

9

La forma más simple de hacerlo es mediante la implementación manual de una máquina de estados finitos. En otras palabras, procese la cadena un carácter a la vez:

  • Cuando golpea un espacio, rompa un token;
  • Cuando llega a un presupuesto, continúe recibiendo caracteres hasta que llegue a otro presupuesto.
+1

Bien estado finito máquina equivale a expresión regular, por lo que podría seguir con eso, ¿verdad? –

+1

Ten en cuenta que es posible que necesites manejar citas escapadas como \ " – jhclark

3

Dependiendo del formato de la cadena original, debería poder usar una expresión regular como parámetro para el método de "división" de Java: Click here for an example.

El ejemplo no usa la expresión regular que necesitaría para esta tarea.

También puede usar this SO thread como guía (aunque está en PHP) que hace algo muy parecido a lo que necesita. Manipular eso podría hacer el truco (aunque tener citas sea parte de la salida o no, puede causar algunos problemas). Tenga en cuenta que la expresión regular es muy similar en la mayoría de los idiomas.

Editar: ir demasiado lejos en este tipo de tareas puede estar por delante de las capacidades de expresiones regulares, por lo que puede necesitar crear un analizador simple.

-2

¿Has intentado dividir por '=' y crear un token de cada par de la matriz resultante?

+0

Esto tiene el mismo problema que la solución .split() mencionada en la pregunta. –

+0

@rajax Esta solución no funciona, pero podrías hacer algo como dividir un espacio, luego revise cada una de las cadenas divididas: si comienza con '(suponiendo que esté bien formateado), simplemente agregue estas cadenas hasta que encuentre una que termine con'. String Tokenziers o una máquina de estados (o usando un apilar si desea permitir múltiples niveles de comillas de anidamiento alternar entre tipos de cotización ala python) puede ser más eficiente, ¡pero esto también puede funcionar! – DivineWolfwood

1

StreamTokenizer puede ayudar, aunque es más fácil de configurar para romper el '=', ya que siempre se romperá en el inicio de una cadena entre comillas:

String s = "Ta=b c='123 456' d=777 e='uij yyy'"; 
StreamTokenizer st = new StreamTokenizer(new StringReader(s)); 
st.ordinaryChars('0', '9'); 
st.wordChars('0', '9'); 
while (st.nextToken() != StreamTokenizer.TT_EOF) { 
    switch (st.ttype) { 
    case StreamTokenizer.TT_NUMBER: 
     System.out.println(st.nval); 
     break; 
    case StreamTokenizer.TT_WORD: 
     System.out.println(st.sval); 
     break; 
    case '=': 
     System.out.println("="); 
     break; 
    default: 
     System.out.println(st.sval); 
    } 
} 

salidas

Ta 
= 
b 
c 
= 
123 456 
d 
= 
777 
e 
= 
uij yyy 

Si omite las dos líneas que convierten caracteres numéricos en alfa, obtendrá d=777.0, que podría serle útil.

-1
java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(line, " "); 
while (tokenizer.hasMoreTokens()) { 
    String token = tokenizer.nextToken(); 
    int index = token.indexOf('='); 
    String key = token.substring(0, index); 
    String value = token.substring(index + 1); 
} 
+0

Esto no funcionará para la cadena de muestra – user101884

+0

Tiene razón. Totalmente espaciado en el espacios en los valores. –

1

Supuestos:

  • Su nombre de variable ('a' en la asignación 'a = b') puede ser de longitud 1 o más
  • Su nombre de variable ('a' en el asignación 'a = b') no puede contener el carácter espacio, cualquier otra cosa está bien. No se requiere
  • validación de su entrada (entrada supone que está en un formato válido = b)

Esto funciona muy bien para mí.

de entrada:

a=b abc='123 456' &=777 #='uij yyy' ABC='slk slk'    [email protected]*#&=456sldSLKD)#(

Salida:

a=b 
abc='123 456' 
&=777 
#='uij yyy' 
ABC='slk slk'    
[email protected]*#&=456sldSLKD)#(

Código:

import java.util.ArrayList; 
import java.util.List; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class RegexTest { 

    // SPACE CHARACTER           followed by 
    // sequence of non-space characters of 1 or more   followed by 
    // first occuring EQUALS CHARACTER  
    final static String regex = " [^ ]+?="; 


    // static pattern defined outside so that you don't have to compile it 
    // for each method call 
    static final Pattern p = Pattern.compile(regex); 

    public static List<String> tokenize(String input, Pattern p){ 
     input = input.trim(); // this is important for "last token case" 
           // see end of method 
     Matcher m = p.matcher(input); 
     ArrayList<String> tokens = new ArrayList<String>(); 
     int beginIndex=0; 
     while(m.find()){ 
      int endIndex = m.start(); 
      tokens.add(input.substring(beginIndex, endIndex)); 
      beginIndex = endIndex+1; 
     } 

     // LAST TOKEN CASE 
     //add last token 
     tokens.add(input.substring(beginIndex)); 

     return tokens; 
    } 

    private static void println(List<String> tokens) { 
     for(String token:tokens){ 
      System.out.println(token); 
     } 
    } 


    public static void main(String args[]){ 
     String test = "a=b " + 
       "abc='123 456' " + 
       "&=777 " + 
       "#='uij yyy' " + 
       "ABC='slk slk'    " + 
       "[email protected]*#&=456sldSLKD)#("; 
     List<String> tokens = RegexTest.tokenize(test, p); 
     println(tokens); 
    } 
} 
0

Esta solución es a la vez general y compacto (que es efectivamente la versión de expresiones regulares de respuesta de Cletus) :

String line = "a=b c='123 456' d=777 e='uij yyy'"; 
Matcher m = Pattern.compile("('[^']*?'|\\S)+").matcher(line); 
while (m.find()) { 
    System.out.println(m.group()); // or whatever you want to do 
} 

En otras palabras, busque todas las ejecuciones de caracteres que son combinaciones de cadenas entre comillas o caracteres no espaciales; las comillas anidadas no son compatibles (no hay caracteres de escape).

3
line.split(" (?=[a-z+]=)") 

da correctamente:

a=b 
c='123 456' 
d=777 
e='uij yyy' 

Asegúrese de adaptar el [a-z +] parte en caso de que los cambios en la estructura llaves.

Editar: esta solución puede fallar miserablemente si hay un carácter "=" en la parte del valor del par.

0
public static void main(String[] args) { 
String token; 
String value=""; 
HashMap<String, String> attributes = new HashMap<String, String>(); 
String line = "a=b c='123 456' d=777 e='uij yyy'"; 
StringTokenizer tokenizer = new StringTokenizer(line," "); 
while(tokenizer.hasMoreTokens()){ 
     token = tokenizer.nextToken(); 
    value = token.contains("'") ? value + " " + token : token ; 
    if(!value.contains("'") || value.endsWith("'")) { 
      //Split the strings and get variables into hashmap 
      attributes.put(value.split("=")[0].trim(),value.split("=")[1]); 
      value =""; 
    } 
} 
    System.out.println(attributes); 
} 

salida: {d = 777, a = b, e = 'uij yyy', c = '123 456'}

En este caso el espacio continuo se truncará a solo espacio en el valor. HashMap aquí atribuido contiene los valores

1

O, con una expresión regular para tokenizing, y una pequeña máquina de estado que sólo se suma la tecla/val a un mapa:

String line = "a = b c='123 456' d=777 e = 'uij yyy'"; 
Map<String,String> keyval = new HashMap<String,String>(); 
String state = "key"; 
Matcher m = Pattern.compile("(=|'[^']*?'|[^\\s=]+)").matcher(line); 
String key = null; 
while (m.find()) { 
    String found = m.group(); 
    if (state.equals("key")) { 
     if (found.equals("=") || found.startsWith("'")) 
      { System.err.println ("ERROR"); } 
     else { key = found; state = "equals"; } 
    } else if (state.equals("equals")) { 
     if (! found.equals("=")) { System.err.println ("ERROR"); } 
     else { state = "value"; } 
    } else if (state.equals("value")) { 
     if (key == null) { System.err.println ("ERROR"); } 
     else { 
      if (found.startsWith("'")) 
       found = found.substring(1,found.length()-1); 
      keyval.put (key, found); 
      key = null; 
      state = "key"; 
     } 
    } 
} 
if (! state.equals("key")) { System.err.println ("ERROR"); } 
System.out.println ("map: " + keyval); 

imprime

map: {d=777, e=uij yyy, c=123 456, a=b} 

Hace alguna comprobación básica de errores y quita las cotizaciones de los valores.

0
import java.io.*; 
import java.util.Scanner; 

public class ScanXan { 
    public static void main(String[] args) throws IOException { 

    Scanner s = null; 

    try { 
     s = new Scanner(new BufferedReader(new FileReader("<file name>"))); 

     while (s.hasNext()) { 
      System.out.println(s.next()); 
      <write for output file> 
     } 
    } finally { 
     if (s != null) { 
      s.close(); 
     } 
    } 
} 
} 
+0

¿Has probado este código? – YoungHobbit

+0

Sí @YoungHobbit Mi entorno de trabajo Linux (Ubuntu 15.01) codificado en sublime3. – jsroyal

Cuestiones relacionadas