2009-05-10 7 views
5


Je me demande si quelqu'un pourrait me aider à comprendre comment analyser une chaîne ayant le format suivant:Parsing une chaîne contenant des caractères échappées en Java

;field1-field2-fieldN;field1-field2-fieldN; 

Chaque enregistrement est délimité par « ; » et chaque champ d'un enregistrement est délimité par '-'. La complication est que les champs individuels peuvent contenir des caractères délimiteurs échappés comme "\;" ou "-". Cela provoque mon code d'analyse simple ci-dessous pour échouer. Donc ce que j'essaie de faire est de trouver des expressions regex qui correspondent aux délimiteurs mais ne correspondent pas aux délimiteurs échappés. Mes connaissances en regex ne sont pas très bonnes mais je m'attendais à trouver une façon de combiner "([^ \;])" et "([;])" pour obtenir ce dont j'ai besoin.

public static List<ParsedRecord> parse(String data) { 
    List<ParsedRecord> parsedRecords = new List<ParsedRecord>(); 
    String[] records = data.split(";"); 
    for (String record : records) { 
     String[] fields = data.split("-"); 
     parsedRecords.add(new parsedRecord(fields)); 
    } 
    return parsedRecords; 
} 

Merci beaucoup d'avance.

Répondre

7

Vous pourriez peut-être affiner votre expression régulière avec séparation comme celui-ci:

split("[^\\];") 

couper à tout ce qui est un « ; ». mais pas si auparavant il y a un "\". Et le même pour les tirets:

split("[^\\]-") 
+0

Merci! J'ai utilisé une combinaison de votre réponse et celle de Jon pour faire fonctionner l'analyseur. Très appréciée! –

+2

Ce n'est pas tout à fait correct ... l'expression régulière définie ci-dessus aura aussi le caractère précédant le délimiteur à supprimer. Alors "bonjour, monde" devient [enfer, monde]. Le meilleur moyen est d'utiliser le regard négatif derrière le mécanisme dans regex: http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas – Alvin

7

Vous ferez probablement mieux de faire l'unescaping et le splitting dans la même passe. Je sais que c'est mal de séparer les deux éléments de fonctionnalité, mais cela évite certains cas bizarres (par exemple, "foo \; bar", où le, suit un backslash mais est toujours un délimiteur).

Voici un code extrêmement simpliste pour l'analyse: il suppose que toute barre oblique inverse signifie en fait "traiter le caractère suivant comme une entrée simple", mais c'est tout.

import java.util.*; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     List<String> parsed = parse(args[0]); 
     for (String x : parsed) 
     { 
      System.out.println(x); 
     } 
    } 

    public static List<String> parse(String text) 
    { 
     List<String> ret = new ArrayList<String>(); 
     StringBuilder current = new StringBuilder(); 
     boolean escaping = false; 

     for (int i=0; i < text.length(); i++) 
     { 
      char c = text.charAt(i); 
      if (escaping) 
      { 
       current.append(c); 
       escaping = false; 
      } 
      else 
      { 
       if (c == '\\') 
       { 
        escaping = true; 
       } 
       else if (c == ';') 
       { 
        ret.add(current.toString()); 
        current = new StringBuilder(); 
       } 
       else 
       { 
        current.append(c); 
       } 
      } 
     } 
     if (escaping) 
     { 
      throw new IllegalArgumentException("Ended in escape sequence"); 
     } 
     ret.add(current.toString()); 
     return ret; 
    } 
} 

(Notez que cela ne fait pas l'affaire de diviser chaque enregistrement en plusieurs champs, mais vous auriez juste besoin de changer ce que vous faites avec « ; » et réagir aussi « - » - le principe est le même)

Questions connexes