2015-06-03 17 views
5

Ich habe hier mit einem Problem zu tun, verursacht durch ein schmutziges Design. Ich bekomme eine Liste von Zeichenfolgen und möchte Attribute daraus analysieren. Leider kann ich die Quelle, in der diese Zeichenfolge erstellt wurde, nicht ändern.Attribute einer Zeichenfolge extrahieren

Beispiel:

String s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars., deactivated=false" 

Jetzt möchte ich die Attribute extrahieren type, languageCode, url, ref, info und deactivated.

Das Problem hier ist das Feld info, deren Text nicht durch Anführungszeichen begrenzt ist. In diesem Feld können auch Kommas vorkommen, sodass ich das Komma am Ende der Zeichenfolge nicht verwenden kann, um herauszufinden, wo die Enden enden.

Zusätzlich enthalten diese Zeichenfolgen nicht immer alle Attribute. type, info und deactivated sind immer vorhanden, der Rest ist optional.

Irgendwelche Vorschläge, wie ich dieses Problem lösen kann?

+0

Ist Reihenfolge dieser Elemente festgelegt? – Pshemo

+1

Wie wäre es mit der Suche nach '=' und dann das einzelne Wort davor als Feldname auswählen. Alles nach dem '=' bis zum nächsten Feldnamen ist der Wert. Dies setzt voraus, dass der Wert nicht '=' enthalten kann - wenn es möglich ist, haben Sie nicht viel zu tun. – xxbbcc

+2

Wenn alle _other_ -Attribute ein etwas vorhersehbares Format haben, könnten Sie diese entfernen und alles für 'info' behalten ... –

Antwort

2

dass Reihenfolge der Elemente Angenommen fixiert Sie Lösung mit regex wie diese

String s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars., deactivated=false"; 

String regex = //type, info and deactivated are always present 
      "type=(?<type>.*?)" 
     + "(?:, languageCode=(?<languageCode>.*?))?"//optional group 
     + "(?:, url=(?<url>.*?))?"//optional group 
     + "(?:, ref=(?<rel>.*?))?"//optional group 
     + ", info=(?<info>.*?)" 
     + ", deactivated=(?<deactivated>.*?)"; 
Pattern p = Pattern.compile(regex); 
Matcher m = p.matcher(s); 
if(m.matches()){ 
    System.out.println("type -> "+m.group("type")); 
    System.out.println("languageCode -> "+m.group("languageCode")); 
    System.out.println("url -> "+m.group("url")); 
    System.out.println("rel -> "+m.group("rel")); 
    System.out.println("info -> "+m.group("info")); 
    System.out.println("deactivated -> "+m.group("deactivated")); 
} 

Output schreiben konnte:

type -> INFO 
languageCode -> EN-GB 
url -> http://www.stackoverflow.com 
rel -> 1 
info -> Text, that may contain all kind of chars. 
deactivated -> false 

EDIT: Version2 regex Suche nach oneOfPossibleKeys=value wo value endet mit:

  • , oneOfPossibleKeys=
  • oder Ende der Saite hat, nachdem es (durch $ dargestellt).

Code:

String s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars., deactivated=false"; 

String[] possibleKeys = {"type","languageCode","url","ref","info","deactivated"}; 
String keysStrRegex = String.join("|", possibleKeys); 
//above will contain type|languageCode|url|ref|info|deactivated 

String regex = "(?<key>\\b(?:"+keysStrRegex+")\\b)=(?<value>.*?(?=, (?:"+keysStrRegex+")=|$))"; 
    // (?<key>\b(?:type|languageCode|url|ref|info|deactivated)\b) 
    // = 
    // (?<value>.*?(?=, (?:type|languageCode|url|ref|info|deactivated)=|$))System.out.println(regex); 

Pattern p = Pattern.compile(regex); 
Matcher m = p.matcher(s); 


while(m.find()){ 
    System.out.println(m.group("key")+" -> "+m.group("value")); 
} 

Ausgang:

type -> INFO 
languageCode -> EN-GB 
url -> http://www.stackoverflow.com 
ref -> 1 
info -> Text, that may contain all kind of chars. 
deactivated -> false 
+0

Ich hatte eine ähnliche Idee wie Ihre Version 2. Aber warum verwenden Sie nicht Ihre 'keysStrRegex' für den eigentlichen Schlüssel, also statt" \\ w + '? –

+0

@tobias_k Das ist eine sehr gute Frage. Antwort aktualisiert – Pshemo

4

Eine mögliche Lösung ist die Suche nach = Zeichen in der Eingabe und dann das einzelne Wort unmittelbar vor dem Feldnamen - es scheint, dass alle Ihre Feldnamen einzelne Wörter sind (kein Leerzeichen). Wenn das der Fall ist, kannst du dann alles nach dem = bis zum nächsten Feldnamen (unter Berücksichtigung der Trennung ,) als Wert nehmen.

Dies setzt voraus, dass der Wert = nicht enthalten kann.

Edit:

Als mögliche Weise = eingebettet zu handhaben, können Sie das Wort vor ihm sehen, wenn man Ihre bekannten Feldnamen ist - wenn nicht, können Sie möglicherweise die = als eingebettetes behandeln Charakter eher als ein Operator. Dies setzt jedoch voraus, dass Sie eine festgelegte Menge bekannter Felder haben (von denen einige nicht immer angezeigt werden). Diese Annahme kann erleichtert werden, wenn Sie wissen, dass bei den Feldnamen zwischen Groß- und Kleinschreibung unterschieden wird.

+2

"Dies setzt voraus, dass der Wert nicht enthalten kann =" Wir brauchen keine so starke Annahme. Wir können auch annehmen, dass '=', das Trennzeichen in 'Schlüssel = Wert 'ist, nur bestimmte Wörter vorangestellt werden können. Wenn es kein vordefiniertes Schlüsselwort davor hat, muss es Teil des Wertes sein. – Pshemo

+0

@Pshemo Heh, ich habe gerade das eingegeben - danke für deinen Kommentar. :) – xxbbcc

1

Sie könnten einen regulären Ausdruck verwenden, der alle "festen" Gruppen erfasst und alles verwendet, was für info übrig bleibt. Dies sollte sogar funktionieren, wenn der info Teil , oder = Zeichen enthält. Hier ist ein kurzes Beispiel (mit Python, aber das sollte kein Problem sein ...).

>>> p = r"(type=[A-Z]+), (languageCode=[-A-Z]+), (url=[^,]+), (ref=\d), (info=.+?), (deactivated=(?:true|false))" 
>>> s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars, even deactivated=true., deactivated=false" 
>>> re.search(p, s).groups() 
('type=INFO', 
'languageCode=EN-GB', 
'url=http://www.stackoverflow.com', 
'ref=1', 
'info=Text, that may contain all kind of chars, even deactivated=true.', 
'deactivated=false') 

Wenn eines dieser Elemente optional sind, können Sie eine ? nach diesen Gruppen setzen, und das Komma optional machen. Wenn die Reihenfolge anders sein kann, ist es komplizierter. In diesem Fall verwenden Sie mehrere RegExes, um die einzelnen Attribute zu erfassen, und entfernen (ersetzen Sie sie mit '') die Zeichen in der Zeichenfolge, bevor sie mit dem nächsten Attribut übereinstimmen. Schließlich passen Sie info.


Bei näherer Betrachtung gegeben, dass diese Attribute beliebiger Reihenfolge haben könnte, kann es vielversprechender sein einfach alles zu erfassen, das sich von einem Schlüsselwort, um zum nächsten, und zwar unabhängig von seiner tatsächlichen Inhalt, sehr ähnlich Pshemo Lösung:

keys = "type|languageCode|url|ref|info|deactivated" 
p = r"({0})=(.+?)(?=\, (?:{0})=|$)".format(keys) 
matches = re.findall(p, s) 

Aber auch dies kann in einigen sehr obskuren Fällen fehlschlagen, z wenn das info Attribut so etwas wie ', ref=foo' enthält, einschließlich des Kommas. An diesen Unklarheiten scheint es jedoch keinen Weg zu geben. Wenn Sie eine Zeichenfolge wie info=in this string, ref=1, and in another, ref=2, ref=1 haben, enthält sie ein ref Attribut oder drei oder gar keine?

Verwandte Themen