Laden...

Komplexen String splitten

Erstellt von math55 vor 16 Jahren Letzter Beitrag vor 16 Jahren 3.085 Views
math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren
Komplexen String splitten

Hallo alle zusammen 🙂,

ich möchte einen string splitten. Der String sieht ungefähr so aus:

_
1N"00000" "." 2N"0000" "." 3N"0" "." 4N"0000"_

was ich jetzt machen möchte, ist die 1N 2N und so weiter rauszusuchen und alles was dahinter steht. die punkte sind nicht immer da, also daran kann ich mich nicht orientieren. es soll also erstmal rauskommen:

1N"00000" "."
2N"0000" "."
3N"0" "."
4N"0000"

das problem ist allerdings, dass der string auch anders aussehen kann, wobei statt dem N auch ein A oder ein E stehen kann. das ist sone art formatierungssache...ja nach buchstaben wird was andres damit gemacht. jemand ne idee?

danke

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

Regex

herbivore

2.921 Beiträge seit 2005
vor 16 Jahren

Du könntest auch eine fertige Tokenizer Klasse herunterladen, aber die meisten sind sowieso wie Herbivore es vorschlägt mit RegEx programmiert.

EDIT: s. z.B. auch hier:

String Tokenizer

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

hallo leute,

das problem ist, dass der string sehr komplex ist. mal noch ein beispiel:

1A"7"N"00000" "." 2A"-1,5" """" 3N"0" """" 4E"3"

es gibt also die kennzeichner A, N und E. wobei eben jeder der 3 etwas anedres aussagt. der string wird auf eine csv datei angewendet, welche ich vorher auslese. sagen wir mal, ich habe die folgenden werte in den spalten in der csv:

_
column 1 : 1
column 2 : 18
column 3 : 10
column 4 : 9_

dann macht der obige string 00008.16,5"10" daraus. das problem ist auch nicht, was er draus macht, sondern lediglich, wie ich es anstellen kann, eine beliebige anzahl solcher aufrufe pro spalte hintereinander zuzulassen.

hier mal, was die kennzeichner machen:

_
N - die zahl dahinter wird als integer behandelt und entsprechend des formats formatiert
A - der wert wird als nummer behandelt und die zahl dahinter wird aufaddiert
E - wenn der wert in der csv nicht leer ist, wird er geleert _

die daten liegen mir in einer arraylist vor. um genau zu sein, im ersten element der arrayliste habe ich eine array mit den gesplitteten werten der ersten zeile und so weiter. meine arrayliste hat also so viele elemente, wie die csv datei zeilen hat. ich hoffe, ich konnte klar machen, worum es mir geht? ich muss parktisch schauen, was alles zur zb ersten spalte zählt, mir dann die entsprechenden werte aus meiner arraylist zusammenpacken und dann eben weiter verwenden.

vielen dank!!!

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

in CSV-Dateien sind ja die Spalten durch ein Trennzeichen getrennt, anhand dessen man Splitten kann. Taucht das Trennzeichen innerhalb von Anführungsstrichen auf, darf dort nicht getrennt werden. Ist sichergestellt, dass letztes nie der Fall sein kann, kann man einfach String.Split verwenden, um die Spalten zu trennen.

herbivore

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

hallo und danke für die antwort. ich hatte mich nicht recht ausgedrückt. also es ist so:

ich habe im prinzip 2 dateien, einmal die csv datei, welche ich zeilenweise auslese, jede zeile mittels split am ; splitte. da kommt ein array raus, welches ich in eine arraylist packe. hat meine csv datei also zb 100 zeilen, würde ein Count über meine arrayliste den wert 100 ausgeben. ich kann also zum beispiel mit


((string[])(liste[0]))[24]

auf das 25 element der ersten zeile zugreifen. das funktioniert alles prima.

die zweite datei ist eine config datei, welche im textformat vorliegt. die daten der csv datei sollen später in eine datenbank geschrieben werden. in der config datei steht dann zum beispiel sowas wie

_
Column_Estate_Zip:24_

was ausdrückt, dass der wert vor dem : in spalte 24 zu finden ist. das kann ich leicht aus meiner liste suchen und in die db schreiben. manchmal, steht aber eben sowas wie der komplexe string da, also eben zb 1N"00000" "." 2N"0000" "." 3N"0" "." 4N"0000". was eben ausdrückt, dass der wert für diese spalte nicht nur in spalte 24 zu finden ist, sondern sich eben über 4 spalten (arrayelemnte) in der csv erstreckt. wobei die anzahl der spalten und die aufrufe der kennzeichner pro spalte beliebig sein können. auch die punkte müssen nicht da sein!

ich muss also den string so parsen, dass ich mir die werte pro spalte hole und entsprechend der parameter (A, E oder N) bearbeite und am ende zusammensetze und ERST DANN in die db schreibe. das ist das problem, das auslesen der dateien funzt.

schwer zu erklären...konnte ich es diesmal besser beschreiben?

danke euch!!

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

ja, aber das ist ja wieder dein Frage vom Anfang, wie man so einen String wie 1N"00000" "." 2N"0000" "." 3N"0" "." 4N"0000" aufteilt. Und da wurde die Antwort doch schon gegeben: Regex.

herbivore

G
84 Beiträge seit 2007
vor 16 Jahren

Ui, hehe - wer hat denn diese Struktur verbrochen? 😉
Nein sag nix - lass mich raten - wir habens hier mit einem Prachtexemplar einer "historisch gewachsenen" Lösung zu tun, stimmts?

Im Endeffekt ist es immer das gleiche bei solchen Stringverarbeitungen.
Du musst Regeln herausfinden, anhand derer Du den String splitten kannst.
Wenn es keine eindeutigen Regeln gibt, ist es nicht möglich den String fehlerfrei zu aufzusplitten.

Aber da würde ich ganz pragmatisch vorgehen - irgendwo muss es ja eine Implementierung geben, die diese Datenstruktur schon verarbeitet ... dort würde ich mir ansehen anhand welcher Regeln das passiert, und diese dann in Deinen Code, bzw. in den RegEx übertragen.

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

hallo, die alte software macht das anders, ich solls aber mit regex machen 🙂. also ich habe mal einen regex zusammengebaut, der auf alle variationen, die auftreten können passt.

das isser:


string line = "1A\"7\"N\"00000\" \".\" 2A\"-1,5\" \"\"\"\" 3N\"0\" \"\"\"\" 4E\"3\"";
string pattern = "((\\d+([a-zA-Z]\"([^\"]|\"\")*\")*|\"([^\"]|\"\")*\")\\s+)+";	

if(Regex.IsMatch(line, pattern))
{
    Console.WriteLine("matcht!!!");
}

aber trotzdem komm ich nicht drauf, wie ich den nu splitten kann, so dass ich für jede spalte die aufrufe bekomme....noch ideen? mit dem regex sollte es doch nicht mehr so schwer sein? nur ich seh den wald vor lauter bäumen nicht mehr.

grüße

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

matcht der Pattern schon? Wenn nicht kannst du On-the-fly Regex-Tester: Regex-Lab benutzen, um den Pattern so zu ändern, dass er matcht.

Das Splitten selbst ist trivial. Du verwendest einfach Gruppen in dem Pattern und liest deren Inhalt mit Match.Groups aus.

herbivore

G
84 Beiträge seit 2007
vor 16 Jahren

hallo, die alte software macht das anders, ich solls aber mit regex machen 🙂.

Jo, da haben wir uns falsch verstanden.
Ich meinte ja nicht, dass Du den alten Code kopieren sollst, sondern die angwandten Bedingungen analysieren und als RegEx umsetzen.
Vorteil: Wiederverwendung bewährter Vorgehensweisen/Lösungen kann Dir seeeehr viel Ärger ersparen - und Fehlersuche in RegEx ist nicht unbedingt erfreulich 😉

Aber wenn Du die richtige Expression auch so schon hast ist ja alles in Butter 😉

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

hi, also ich hab den regex, der eine zeile erkennt, aber ich muss noch splitten! das bekomm ich aber nicht hin...ich dachte nur, dass wenn ich denr egex habe, der dasd findet, dann sollte doch das splitten kein ding mehr sein, ich komm aber nicht drauf...brauch also nochmal hilfe.

grüße

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

ich habe ja schon geschrieben, wie das splitten geht.

Im On-the-fly Regex-Tester: Regex-Lab kannst du dir auch vorab angucken, welche Gruppen was enthalten.

herbivore

G
84 Beiträge seit 2007
vor 16 Jahren

Hm, herbivore hat doch was dazu geschrieben ...

Ich hab leider bisher keine RegEx nur unter C benutzt, daher kann ich dir bei der konkreten Implementierung net helfen.

Aber unser Freund und Helfer MSDN hat doch da ein supereinfaches Beispiel dazu!?

public class Test
{

    public static void Main ()
    {

        // Define a regular expression for repeated words.
        Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b",
          RegexOptions.Compiled | RegexOptions.IgnoreCase);

        // Define a test string.        
        string text = "The the quick brown fox  fox jumped over the lazy dog dog.";
        
        // Find matches.
        MatchCollection matches = rx.Matches(text);

        // Report the number of matches found.
        Console.WriteLine("{0} matches found.", matches.Count);

        // Report on each match.
        foreach (Match match in matches)
        {
            string word = match.Groups["word"].Value;
            int index = match.Index;
            Console.WriteLine("{0} repeated at position {1}", word, index);   
        }
        
    }
	
}

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

oh, das hatte ich nicht gesehen. also ich kann matchen....ABER ich finde erstmal nur alle strings, die nach dem hier verwendetetn schema aufgebaut sind. ich finde NICHT die einzelnen teile für die spalten, oder reden wir aneinander vorbei? also mein regex da oben findet alle strings, die so ein format enthalten, ich bekomme es aber nicht hin, auch nicht mit gruppen, die einzelnen teile rauszusuchen....

grüße

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

es ist ja auch kein echtes Spliten, des originalen Strings. Du liest einfach aus dem Match-Objekt die Teile aus, die dich interessieren.

Und wie gesagt, mit On-the-fly Regex-Tester: Regex-Lab kannst du ja sehr einfach sehen, was in dem Match-Objekt enthalten ist Groups/Captures.

herbivore

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

der funzt bei mir nicht, da ich version 1.1 nutzen muss 😦. könnt ihr mir mal ein beispiel geben, wie ich den matcher in diesem fall nutze?

grüße und danke 🙂

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

oben ist doch schon ein Beispiel. Gut möglich, dass in der :rtfm: Doku weitere sind.

Und für Regex-Lab brauchst du ja nur die 2.0er Runtime. Die kannst du problemlos neben 1.1 installieren.

herbivore

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

klasse, jetzt läuft das tool. ist nett 🙂! jetzt noch eine frage. ich habe ja jetzt in dem toll unten groups stehen und captures. wie komm ich jetzt aber über die groups an die captures? ihr wisst, was ich meine? also sagen wir mal group 2 und alle captures...oder versteh ich da was nicht?

grüße

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

habs 😁

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

Groups und Captures sind ja einfach Collections/Arrays. Der Match enthält die Groups und jede Group enthält ihre Captures. Du kannst z.B. so auf ein bestimmtes Capture zugreifen:

m.Groups [x].Captures[y]

Alles weitere sollte sich daraus ableiten lassen.

herbivore

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

hallo leute,

ich habe da was mit match und regex noch nicht ganz geschnallt. hier erstmal mein code:


Regex rx = new Regex("(?<n>\\d+)((?<f>[a-zA-Z])\"(?<a>([^\"]|\"\")*)\")*|\"(?<s>([^\"]|\"\")*)\"",
		RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
			
string text = "1A\"7\"N\"00000\" \".\" 2A\"-1,5\" \"\"\"\" 3N\"0\" \"\"\"\" 4E\"3\" ";
//string text = "14 \" \" 16 ";
        	
Match m = rx.Match(text);
			
while (m.Success) 
{
				
    for(int i=0;i<m.Groups.Count;i++) // was muss hier hin????
    {
					
        Group g = m.Groups[i];
	int groupIndex = g.Index;	

	CaptureCollection cc = g.Captures;
					
	for (int j = 0; j < cc.Count; j++) 
	{
	     Capture c = cc[j];
	     System.Console.WriteLine("Capture " + j + " = " + c.Value + " --> " +           groupIndex);
	}
					
    }

    m = m.NextMatch();
				
}

wie kann ich jetzt zum beispiel rausbekommen wie oft ich durchlaufen muss? es nützt mir ja nix, wenn ich m.Groups.Count nehme, da das ja in diesem fall immer 5 ist. kommentiert man die eine auskommentierte zeile ein, klappts nicht mit dem durchlaufen, bzw. er läuft zuerst durch. jetzt hab ich zwar alles fein getrennt, aber so richtig passen tut das immer noch nicht...ich brauch die werte einzeln aber in der richtigen reihenfolge. der string kann eben seeeehr unterschiedlich sein. ich denke mal, man kann über den index festlegen, welche kombinationen zusammengehören und welche nicht...aber ich krieg das nicht so recht hin...

jemand ideen?

danke!!! 😁

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

schwer zu beantworten, wenn wenn du nicht sagst, welche Werte du brauchst.

Eine Schleife über Groups bei 0 zu beginnen ist meistens nicht sinnvoll, weil Group 0 eine Pseudogruppe ist, die den gesamten Match enthält.

Außerdem hast du doch deine Gruppen nett benannt. Warum holst du dir nicht gezielt die Werte, die du brauchst?

herbivore

math55 Themenstarter:in
314 Beiträge seit 2007
vor 16 Jahren

hi, also wenn ich das hier ausführe, bekomme ich genau das, was zu der jeweiligen spalte gehört und was dazwischen soll.


Regex rx = new Regex("(?<n>\\d+)((?<f>[a-zA-Z])\"(?<a>([^\"]|\"\")*)\")*|\"(?<s>([^\"]|\"\")*)\"",
				RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
			
string text = "1A\"7\"N\"00000\" \".\" 2A\"-1,5\" \"\"\"\" 3N\"0\" \"\"\"\" 4E\"3\" ";
        	
Match m = rx.Match(text);
			
while (m.Success) 
{
				
    Group g = m.Groups[0];
    int groupIndex = g.Index;	

    CaptureCollection cc = g.Captures;
					
     for (int j = 0; j < cc.Count; j++) 
    {
	Capture c = cc[j];
	System.Console.WriteLine("Capture " + j + " = " + c.Value + " --> " + groupIndex);
     }
				
    m = m.NextMatch();
}

nämlich

_
1A"7"N"00000"
"."
2A"-1,5"
""""
3N"0"
""""
4E"3"_

allerdings müsste ich das jetzt nochmals parsen, da im ersten eben nochmal das N vorkommt. aber das ist aufjeden fall erstmal alles nach spalten getrennt. ich habe j auch meine benannten gruppen (?<a> etc)....aber damit ists mir dann wieder ZU auseinander. ich müsste jetzt als nächstes sagen wir mal den ersten teil nehmen und damit folgendes anstellen:

  1. hole den wert in der csv aus spalte 1 (1) --> ist sagen wir mal 10
  2. addiere 7 dazu (A"7")
  3. und formatiere das ganze entsprechend den angaben hinter N (N"00000")
  4. dann würde ich den . (".") dahinter packen und so weiter.

raus kommt dann eben 00017. ist ja auch alles schön und gut, nur ist der string eben sowas von unterschieldich....ich komm nicht auf eine einheiltiche art und weise damit umzugehen.

ihr könnt mir noch folgen?

grüße und vielen vielen dank!! =)

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo math55,

ihr könnt mir noch folgen?

nein, nicht wirklich. Du hast für jede Spalte einen Match (pro Schleifendurchlauf einen) und innerhalb des Matches benannte Gruppen, für die Werte, die dich (in dieser Spalte) interessieren. Was brauchst du mehr? Ich denke, das ist alles was du brauchst.

herbivore