2009-10-01 12 views
9

voglio tokenize una stringa come questaTokenize una stringa con uno spazio in Java

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

non riesco a Spalato sulla base di come questo

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

Qualsiasi idea di come posso dividere in modo che ricevo gettoni come

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

Non potresti usare una regex per dividere per spazi a meno che tu non sia all'interno di una citazione (non che io sappia regex, ma sono abbastanza sicuro che puoi farlo). – mk12

+0

Il tuo codice funziona perfettamente qui usando jdk 1.6.0_13 –

+0

@LePad sopra il codice verrà emesso * [a = b, c = '123, 456', d = 777, e = 'uij, yyy'] * –

risposta

9

Il modo più semplice per farlo è quello di implementare manualmente una semplice macchina a stati finiti. In altre parole, elabora la stringa un carattere alla volta:

  • Quando colpisci uno spazio, interrompi un token;
  • Quando premi una citazione, continua a ricevere caratteri finché non premi un'altra citazione.
+1

Stato ben definito la macchina equivale all'espressione regolare, quindi potresti semplicemente limitarti a farlo, giusto? –

+1

Attenzione che potrebbe essere necessario gestire le virgolette con escaping come \ " – jhclark

3

A seconda della formattazione della stringa originale, si dovrebbe essere in grado di utilizzare un'espressione regolare come parametro per il metodo "split" java: Click here for an example.

L'esempio non utilizza l'espressione regolare necessaria per questa attività.

È inoltre possibile utilizzare this SO thread come linea guida (anche se è in PHP) che fa qualcosa di molto vicino a ciò che è necessario. Manipolare leggermente questo potrebbe fare il trucco (anche se avere le virgolette essere parte dell'output o non può causare alcuni problemi). Tieni presente che regex è molto simile nella maggior parte delle lingue.

Modifica: andare troppo oltre in questo tipo di attività potrebbe essere in anticipo rispetto alle funzionalità di regex, quindi potrebbe essere necessario creare un parser semplice.

-2

Hai provato a dividere con '=' e creare un token da ciascuna coppia dell'array risultante?

+0

Questo ha lo stesso problema della soluzione .split() menzionata nella domanda: –

+0

@rajax Questa soluzione non funziona, ma si potrebbe fare qualcosa come split off uno spazio, quindi passa attraverso ciascuna delle stringhe divise: se inizia con '(supponendo che sia ben formattato), basta aggiungere queste stringhe insieme finché non ne trovi una che termina con'. String Tokenziers o una macchina a stati (o usando un stack se vuoi consentire più livelli di nidificazione di virgolette alternando i tipi di quote ala python) potrebbe essere più efficiente, ma anche questo può funzionare! – DivineWolfwood

1

StreamTokenizer può aiutare, anche se è più facile da configurare per rompere il '=', in quanto sarà sempre rompere all'inizio di una stringa tra virgolette:

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); 
    } 
} 

uscite

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

Se si omettono le due righe che convertono caratteri numerici in alfa, si ottiene d=777.0, che potrebbe essere utile.

-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

Questo non funziona per la stringa di esempio – user101884

+0

Hai ragione. spazi nei valori –

1

Ipotesi:

  • Il tuo nome di variabile ('a' nell'assegnazione 'a = b') può essere di lunghezza 1 o più
  • Il tuo nome di variabile ('a' nella assegnazione 'a = b') non può contenere il carattere dello spazio, qualsiasi altra cosa va bene.
  • convalida del vostro ingresso non è richiesta (ingresso presume essere in un valido = Formato b)

Questo funziona bene per me.

ingresso:

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

uscita:

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

Codice:

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

Questa soluzione è sia generale e compatta (è effettivamente la versione regex di risposta 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 
} 

In altre parole, trova tutte le serie di caratteri che sono combinazioni di stringhe tra virgolette o caratteri non spaziali; le virgolette nidificate non sono supportate (non esiste un carattere di escape).

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

dà correttamente:

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

Assicurarsi di adattare la parte [a-z +] nel caso in cui le modifiche chiavi della struttura.

Modifica: questa soluzione può fallire miseramente se c'è un carattere "=" nella parte valore della coppia.

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); 
} 

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

In questo caso spazio continuo verrà troncato a singolo spazio nel valore. hashmap qui attribuito contiene i valori

1

Oppure, con una regex per la creazione di token, e una macchina a stati poco che aggiunge solo la chiave/val a una mappa:

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); 

stampe fuori

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

Fa un controllo di base dell'errore e prende le virgolette dai valori.

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

Hai testato questo codice? – YoungHobbit

+0

Sì @YoungHobbit Il mio ambiente di lavoro Linux (Ubuntu 15.01) codificato su sublime3. – jsroyal

Problemi correlati