2016-11-17 4 views
1

I Extrahieren von Daten aus einem Textstrom ist, die Daten als solcheRegexMuster Gruppen Übereinstimmen mit Muster beginnend

strukturiert sind
/1-<id>/<recType>-<data>..repeat n times../1-<id>/#-<data>..repeat n times.. 

In dem obigen, der „/ 1“ Feld vorangeht, den Datensatz, der dann haben kann eine beliebige Anzahl von folgenden Bereichen, die jeweils mit einer Auswahl an recType 2-9 (auch beginnt jedes Feld mit einem „/“)

Zum Beispiel:

/1-XXXX/2-YYYY/9-ZZZZ/1-AAAA/3-BBBB/5-CCCC/8=NNNN/9=DDDD/1-QQQQ/2-WWWW/3=PPPP/7-EEEE 

So gibt es drei Gruppen von d ata über

1=XXXX 2=YYYY 9=ZZZZ 
1=AAAA 3=BBBB 5=CCCC 8=NNNN 9=DDDD 
1=QQQQ 2=WWWW 3=PPPP 7=EEEE 

Die Daten sind der Einfachheit halber, ich weiß sicher, dass es nur [A-Z0-9 enthält. ] Kann aber mit variabler Länge (nicht nur 4 Zeichen wie pro Beispiel)

nun die folgenden Ausdruck Art von Werken sein, aber es ist nur die ersten 2 Felder jeder Gruppe und keiner der übrigen Felder erfassen ...

/1-(?'fld1'[A-Z]+)/((?'fldNo'[2-9])-(?'fldData'[A-Z0-9\. ]+)) 

Ich weiß, ich brauche irgendwo eine Art von quantifier, aber ich weiß nicht, was oder wo es zu platzieren.

+0

Welches Werkzeug oder Sprache verwenden Sie? – Toto

+0

Visual Studio - C#. NET –

Antwort

1

Sie können eine Regex verwenden, um diese Blöcke mit 2. NET Regex-Funktionen übereinstimmen: 1) Sammlung erfassen und 2) mehrere Erfassungsgruppen mit dem gleichen Namen im Muster. Dann werden wir einige Linq Magie brauchen, um die erfassten Daten in eine Liste von Listen zu kombinieren:

(?<fldNo>1)-(?'fldData'[^/]+)(?:/(?<fldNo>[2-9])[-=](?'fldData'[^/]+))* 

Einzelheiten:

  • (?<fldNo>1) - Gruppe fldNo passend 1
  • - - einen Bindestrich
  • (?'fldData'[^/]+) - Gruppe "fldData" erfasst 1+ Zeichen außer /
  • (?:/(?<fldNo>[2-9])[-=](?'fldData'[^/]+))* - null oder mehr Sequenzen von:
    • / - Literal /
    • (?<fldNo>[2-9]) - 2 zu 9 Ziffer (Gruppe "fldNo")
    • [-=] - a - oder =
    • (?'fldData'[^/]+) - 1+ Zeichen außer / (Gruppe "fldData")

anzeigen regex demo, Ergebnisse:

enter image description here

Siehe C# demo:

using System; 
using System.Linq; 
using System.Text.RegularExpressions; 
public class Test 
{ 
    public static void Main() 
    { 
     var str = "/1-XXXX/2-YYYY/9-ZZZZ/1-AAAA/3-BBBB/5-CCCC/8=NNNN/9=DDDD/1-QQQQ/2-WWWW/3=PPPP/7-EEEE"; 
     var res = Regex.Matches(str, @"(?<fldNo>1)-(?'fldData'[^/]+)(?:/(?<fldNo>[2-9])[-=](?'fldData'[^/]+))*") 
      .Cast<Match>() 
      .Select(p => p.Groups["fldNo"].Captures.Cast<Capture>().Select(m => m.Value) 
        .Zip(p.Groups["fldData"].Captures.Cast<Capture>().Select(m => m.Value), 
         (first, second) => first + "=" + second)) 
      .ToList(); 
      foreach (var t in res) 
       Console.WriteLine(string.Join(" ", t)); 
    } 
} 
+0

Magic .... Danke. –

+0

Ich wünschte, ich könnte mehr als eine Upvote geben ... nur für die Vollständigkeit der Antwort –

+0

Bitte nicht upvote mehr als 2 Antworten von ein und derselben Person pro Tag, das gilt als Massen-Upvoting und die Aktionen sind die nächsten zurückgesetzt Tag. Ein Upvote pro Antwort ist genug. –

0

Eine einzelne Regex nicht das optimale Werkzeug ist, dies zu tun (zumindest auf diese Weise verwendet). Der Hauptgrund ist, dass Ihr Stream eine variable Anzahl von Einträgen enthält und eine variable Anzahl von Capture-Gruppen nicht unterstützt wird. Ich bemerkte auch, dass einige der Werte "=" zwischen ihnen hatten, ebenso wie der Bindestrich, den deine aktuelle Regex nicht anspricht. Das Problem tritt auf, wenn Sie versuchen, einen Quantifizierer zu einer Erfassungsgruppe hinzuzufügen - die Gruppe merkt sich nur das Letzte, was sie erfasst hat. Wenn Sie also einen Quantifizierer hinzufügen, wird das erste und letzte Feld abgefangen der ganze Rest von ihnen. So etwas wie das wird nicht funktionieren:

\/1-(?'fld1'[A-Z]+)(?:\/(?'fldNo'[2-9])[-=](?'fldData'[A-Z]+))+ 

Wenn Ihre Ströme waren alle die gleiche Länge, dann eine einzelne Regex verwendet werden könnte, aber es gibt einen Weg, um es mit einer foreach-Schleife mit einem viel einfacheren regex Arbeits zu tun auf jedem Teil deines Streams (so überprüft es auch deinen Stream, wenn es weitergeht!)

Jetzt bin ich nicht sicher, mit welcher Sprache du arbeitest, wenn du dies verwendest, aber hier ist eine Lösung in PHP, die ich Denken liefert, was Sie brauchen.

function extractFromStream($str) 
{ 
    /* 
    * Get an array of [num]-[letters] with explode. This will make an array that 
    * contains [0] => 1-AAAA, [1] => 2-BBBB ... etc 
    */ 

    $arr = explode("/", substr($str, 1)); 

    $sorted = array(); 
    $key = 0; 

    /* 
    * Sort this data into key->values based on numeric ordering. 
    * If the next one has a lower or equal starting number than the one before it, 
    * a new entry will be created. i.e. 2-aaaa => 1-cccc will cause a new 
    * entry to be made, just in case the stream doesn't always start with 1. 
    */ 

    foreach ($arr as $value) 
    { 
     // This will get the number at the start, and has the added bonus of making sure 
     // each bit is in the right format. 
     if (preg_match("/^([0-9]+)[=-]([A-Z]+)$/", $value, $matches)) { 
      $newKey = (int)$matches[1]; 
      $match = $matches[2]; 
     } else 
      throw new Exception("This is not a valid data stream!"); 

     // This bit checks if we've got a lower starting number than last time. 
     if (isset($lastKey) && is_int($lastKey) && $newKey <= $lastKey) 
      $key += 1; 

     // Now sort them.. 
     $sorted[$key][$newKey] = $match; 

     // This will be compared in the next iteration of the loop. 
     $lastKey = $newKey; 
    } 

    return $sorted; 
} 

Hier ist, wie Sie es verwenden können ...

$full = "/1-XXXX/2-YYYY/9-ZZZZ/1-AAAA/3-BBBB/5-CCCC/8=NNNN/9=DDDD/1-QQQQ/2-WWWW/3=PPPP/7-EEEE"; 

try { 
    $extracted = extractFromStream($full); 
    $stream1 = $extracted[0]; 
    $stream2 = $extracted[1]; 
    $stream3 = $extracted[2]; 

    print "<pre>"; 
    echo "Full extraction: \n"; 
    print_r($extracted); 
    echo "\nFirst Stream:\n"; 
    print_r($stream1); 
    echo "\nSecond Stream:\n"; 
    print_r($stream2); 
    echo "\nThird Stream:\n"; 
    print_r($stream3); 
    print "</pre>"; 
} catch (Exception $e) { 
    echo $e->getMessage(); 
} 

Diese

Full extraction: 
Array 
(
    [0] => Array 
     (
      [1] => XXXX 
      [2] => YYYY 
      [9] => ZZZZ 
     ) 

    [1] => Array 
     (
      [1] => AAAA 
      [3] => BBBB 
      [5] => CCCC 
      [8] => NNNN 
      [9] => DDDD 
     ) 

    [2] => Array 
     (
      [1] => QQQQ 
      [2] => WWWW 
      [3] => PPPP 
      [7] => EEEE 
     ) 

) 

First Stream: 
Array 
(
    [1] => XXXX 
    [2] => YYYY 
    [9] => ZZZZ 
) 

Second Stream: 
Array 
(
    [1] => AAAA 
    [3] => BBBB 
    [5] => CCCC 
    [8] => NNNN 
    [9] => DDDD 
) 

Third Stream: 
Array 
(
    [1] => QQQQ 
    [2] => WWWW 
    [3] => PPPP 
    [7] => EEEE 
) 

druckt So können Sie sehen Sie die Zahlen als Arrayschlüssel haben, und die Werte, die sie entsprechen, die nun für die weitere Verarbeitung leicht zugänglich sind.Ich hoffe, das hilft Ihnen :)

Verwandte Themen