Regex - Bis zum nächsten Datum oder Ende

  • C#

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Regex - Bis zum nächsten Datum oder Ende

    Ich habe aktuell dieses Pattern ​(\d{1,2}\.\d{1,2}\.\d{1,4}|\d{1,2}\.\d{1,2})
    Es findet ein Datum, dass entweder so 11.11.12 oder so 11.11 aussieht.

    Ich habe folgenden Input.
    20.3. wir haben über blablabla gesprochen, am
    dawdawdad
    22.4. wieder mal kontakt, bm

    Ich möchte nun gerne den Text zu jedem Datum haben (Also den bis zum nächsten Datum oder zum Ende)
    Dieser kann auch mehrzeilig sein.

    Ich habe folgendes Pattern probiert um erstmal das erste Beispiel zu bekommen.
    (\d{1,2}\.\d{1,2}\.\d{1,4}|\d{1,2}\.\d{1,2}).*?​(\d{1,2}\.\d{1,2}\.\d{1,4}|\d{1,2}\.\d{1,2})
    Funktioniert aber nicht. Habe bereits mit zwei Regextestern getestet.

    Kennt da jemand von euch irgendetwas schickes?
    Nimm doch folgendes Pattern und nutze es als Seperator für die Regex.Split-Methode:

    Quellcode

    1. ^(?=\d{1,2}\.\d{1,2}\.\d{1,4}|\d{1,2}\.\d{1,2})

    Wenn's funktioniert, solltest du alle deine Texte mit führendem Datum in einer Liste haben und der Rest wird ganz einfach.
    @Higlav Es will nicht so Recht. Kriege hier 11 Ergebnisse.

    C#-Quellcode

    1. string test = "01.02.03 das ist ein test blablabla 02.03 noch ein test blaaaa 04.03.02 lalalala";
    2. Regex r = new Regex(@"(?=\d{1,2}\.\d{1,2}\.\d{1,4}|\d{1,2}\.\d{1,2})", RegexOptions.Multiline);
    3. string[] result = r.Split(test);


    Was mache ich falsch? Liegt das am Look Behind?
    Mach mal ein Beispiel mit Input und Output

    aber um die zwei Möglichkeiten mal abzudecken:

    Wenn du nur die Datumse (xd) willst:

    C#-Quellcode

    1. string test = "01.02.03 das ist ein test blablabla 02.03 noch ein test blaaaa 04.03.02 lalalala";
    2. foreach (var item in gibHerDatumse(test))
    3. {
    4. MessageBox.Show(item.ToString());
    5. }
    6. ...
    7. private IEnumerable<DateTime> gibHerDatumse(string pQuelle)
    8. {
    9. DateTime lDate;
    10. foreach (var item in pQuelle.Split(' '))
    11. {
    12. if (DateTime.TryParse(item, out lDate))
    13. yield return lDate;
    14. }
    15. }


    Wenn du das zwischen zwei Datumsen willst:

    C#-Quellcode

    1. string test = "01.02.03 das ist ein test blablabla 02.03 noch ein test blaaaa 04.03.02 lalalala";
    2. foreach (var item in getItems(test.Split(' ')))
    3. {
    4. MessageBox.Show(item.ToString());
    5. }
    6. ...
    7. private IEnumerable<string> getItems(string[] pList)
    8. {
    9. DateTime lDate;
    10. for (int iIterate = 0; iIterate < pList.Count(); iIterate++)
    11. {
    12. if (DateTime.TryParse(pList[iIterate], out lDate))
    13. {
    14. int lStart = iIterate;
    15. int lEnd = lStart + 1;
    16. List<string> lElement = new List<string>();
    17. while (!(lEnd > pList.Count() - 1) && !DateTime.TryParse(pList[lEnd], out lDate))
    18. {
    19. lElement.Add(pList[lEnd]);
    20. lEnd++;
    21. }
    22. yield return string.Concat(lElement);
    23. }
    24. }
    25. }


    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RushDen“ ()

    Ich hatte in meinem Pattern noch ein "^" stehen. Das hätte noch den Zeilenumbruch darstellen sollen, stimmt aber nicht ganz: Wenn du immer einen Zeilenumbruch und dann das Datum hast, musst du noch ein "\n" vorne anhängen.
    Ich brauche beide Infos auf dem Datensatz.
    Beispiel.

    Eingabe
    12.01 Hier steht ein Text hier auch 13.01.2014 Blabla 12.01 Noch ein Text
    Ausgabe
    12.01 - Hier steht ein Text hier auch
    13.01.2014 - Blabla
    12.01 - Noch ein Text

    Ich habe mal das hier probiert.

    C#-Quellcode

    1. public struct Note
    2. {
    3. public DateTime Date { get; set; }
    4. public string Text { get; set; }
    5. }
    6. private IEnumerable<Note> getItems(string[] pList)
    7. {
    8. DateTime lDate;
    9. for (int iIterate = 0; iIterate < pList.Count(); iIterate++)
    10. {
    11. if (DateTime.TryParse(pList[iIterate], out lDate))
    12. {
    13. int lStart = iIterate;
    14. int lEnd = lStart + 1;
    15. List<string> lElement = new List<string>();
    16. while (!(lEnd > pList.Count() - 1) && !DateTime.TryParse(pList[lEnd], out lDate))
    17. {
    18. lElement.Add(pList[lEnd]);
    19. lEnd++;
    20. }
    21. yield return new Note() { Date = lDate, Text = string.Concat(lElement) };
    22. }
    23. }
    24. }


    C#-Quellcode

    1. string test = "01.02.03 das ist ein test blablabla 02.03 noch ein test blaaaa 04.03.02 lalalala";
    2. foreach (var item in getItems(test.Split(' ')))
    3. {
    4. MessageBox.Show(item.Date.ToShortDateString() + " " + item.Text);
    5. }


    Bekomme da aber die falschen Daten. (Datum passt nicht)
    also suchst du nach 2 Ziffern, gefolgt von einem Punkt, gefolgt von 2 Ziffern, optional gefolgt von einem Punkt und 4 Ziffern.

    Das muss doch mit Regex machbar sein?

    Edit: naja - so wie dus anfängst müsste es ja eiglich auch gehen ohne Regex

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    LaMiy schrieb:

    C#-Quellcode

    1. test.Split(' ')
    Die Elemente dieses Arrays musst Du alle dem Test DateTime.TryParse() unterwerfen und bei positivem Ausgang verwenden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Das sieht schon mal gar nicht so schlecht aus.
    Aber bei so einem String klappt es nicht. 01.02.2014 das ist ein test 1 blablabla 02.03.1222 noch ein test blaaaa 04.03.02 lalalala
    Ich würde es gerne mit Regex machen, aber ich kenn mich nicht gut genug aus.

    Verschriftlich müsste es ja erstmal heißen.
    Hol mir alles von einem Datum bis zum nächsten Datum oder zum Ende des Strings.
    Die Gruppierungen kann man dann ja nachher machen. Weiß da jemand was?

    Kann doch eigentlich nicht sein, dass es für so ein elementaren Problem einen String in Datum und Text zu gruppieren keine leichte Lösung gibt :)
    Verstehst du denn überhaupt kein Regex ;) ? Mein Regex besteht schon aus 2 Gruppen, dem Datum und dem Text. Willst du nun bis zum nächsten Datum oder zum Ende der Zeile matchen, musst du nur nen Lookahead hinten anfügen, der entweder ein Datum oder ein Zeilenende matcht: (?<Date>(\d\d?)\.(\d\d?)(\.(\d{4})|(\d\d?))?)(?<Name>\D+)(?<=$|((\d\d?)\.(\d\d?)(\.(\d{4})|(\d\d?))?))​
    und ist immer noch unsicher, denn endgültige Sicherheit, ob ein String-Stück parseable ist, kann nur die TryParse-Methode liefern.
    Da ists imo gar nicht so verkehrt, den Text wortweise zu durchlaufen, wie in post#9 begonnen.
    Nur muss man die Logik richtig auf die Reihe kriegen, aber das wird man auch müssen, wenn man son komplexen Regex einsetzt.
    Gugget mal mein Gebastel:

    C#-Quellcode

    1. string test = "01.02.03 das ist ein test blablabla 02.03 noch ein test blaaaa 04.03.02 lalalala";
    2. var notes = GetNotes(test.Split(' ')).ToArray();
    3. //...
    4. public struct Note {
    5. public DateTime Date { get; set; }
    6. public string Text { get; set; }
    7. }
    8. private IEnumerable<Note> GetNotes(string[] words) {
    9. DateTime dt, dt2 = default(DateTime);
    10. var wrds = new List<string>();
    11. foreach (var wrd in words) {
    12. if (DateTime.TryParse(wrd, out dt)) {
    13. if (dt2 != default(DateTime)) {
    14. yield return new Note() { Date = dt2, Text = string.Join(" ", wrds) };
    15. wrds.Clear();
    16. }
    17. dt2 = dt;
    18. } else wrds.Add(wrd);
    19. }
    20. if (dt2 == default(DateTime)) yield break;
    21. yield return new Note() { Date = dt2, Text = string.Join(" ", wrds) };
    22. }

    Da @LaMiy mich darum gebeten hat, hier noch eine funktionierende Lösung mit Regex:

    C#-Quellcode

    1. string data = @"01.02.2014 das ist ein test 1 blablabla 02.03.1222 noch ein test blaaaa 04.03.02 lalalala 04.12 blablabla";
    2. List<Tuple<string, string>> output = new List<Tuple<string, string>>();
    3. Match lastMatch = null;
    4. foreach (Match match in Regex.Matches(data, @"\d{2}\.\d{2}(\.(\d{4}|\d{2})?)?"))
    5. {
    6. if (lastMatch != null)
    7. output.Add(Tuple.Create(lastMatch.Value, data.Substring(lastMatch.Index + lastMatch.Length, match.Index - lastMatch.Index - lastMatch.Length)));
    8. lastMatch = match;
    9. }
    10. output.Add(Tuple.Create(lastMatch.Value, data.Substring(lastMatch.Index + lastMatch.Length)));

    Ich habs jetzt so gemacht, weil Lookahead und mein Datepattern sich irgendwie in die Quere gekommen sind und das hier eigentlich ne ziemlich simple Lösung ist: Alle Dates finden & den Text dazwischen zuordnen. Auf jeden Fall ist es simpler als EDR seine Lösung (die man allerdings auch noch vereinfachen könnte).
    kannst du vlt. eine richtige Methode davon machen, die auch IEnumerable<Note> zurückgibt?
    Dann präsentiere ich dir auch gleich eine Fehler-Möglichkeit, an die du vmtl. nicht gedacht hast.
    Das ist eben das Risiko bei Regex, dass da sich oft immer noch Fail-Möglichkeiten verstecken.

    Kanns eiglich auch gleich zeigen: Wie kämst du mit diesem TestString klar?

    C#-Quellcode

    1. string data = @"Was passiert bei 00.00.0000 ? :P ";

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Der String geht rein und folgendes kommt raus: "00.00.0000", " ? :P ". Hier ist halt die Frage, ob es sich aus der Sicht von LaMiy um ein valides Datum handelt. Ich habe es zwar nicht getestet, allerdings bin ich mir 99% sicher, dass das Framework "nope" und irgendeine Exception schmeißen wird, obwohl das theoretisch ein Datum wäre.
    ich habs getestet, es ist kein valides Datum. Also wenn damit eine Note gebildet wird, stürztes entweder ab oder du hast korrupte Daten.
    Also wie ich's sehe, kommst du auch an komplexerer Verarbeitung mit TryParse nicht dran vorbei, der nächste Fail ist nämlich auch schon vorprogrammiert: "31.11.2014"

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Ich hab es nun so gemacht. Danke an alle.

    C#-Quellcode

    1. /// <summary>
    2. /// Stellt eine Klasse zum Parsen von Telefonnotizen dar.
    3. /// </summary>
    4. public struct Note
    5. {
    6. public string Date { get; set; }
    7. public string Text { get; set; }
    8. public static IEnumerable<Note> GetNotes(string input)
    9. {
    10. // Ausgabe
    11. List<Note> output = new List<Note>();
    12. // Alle Zeilen holen
    13. string[] lines = input.Split('\n');
    14. // Regex für das Datum
    15. Regex rDate = new Regex(@"^((\d{1,2}\.\d{1,2}\.\d{1,4})|(\d{1,2}\.\d{1,2}))");
    16. // Speicher
    17. StringBuilder buffer = new StringBuilder();
    18. // Datum
    19. string date = null;
    20. // Jede Zeile durchgehen
    21. foreach (string line in lines)
    22. {
    23. // Datum holen
    24. Match match = rDate.Match(line);
    25. // Wenn die Zeile nur Text ist
    26. if (match == null || match.Value.Length == 0)
    27. {
    28. buffer.Append(line);
    29. }
    30. else
    31. {
    32. if (!string.IsNullOrEmpty(date) && buffer.Length > 0)
    33. {
    34. // Altes Datum und alten Text speichern
    35. output.Add(new Note() { Date = date, Text = buffer.ToString() });
    36. }
    37. // Neues Datum zuweisen
    38. date = match.Value;
    39. buffer.Clear();
    40. buffer.Append(line.Replace(date, ""));
    41. }
    42. }
    43. if (!string.IsNullOrEmpty(date) && buffer.Length > 0)
    44. {
    45. // Altes Datum und alten Text speichern
    46. output.Add(new Note() { Date = date, Text = buffer.ToString() });
    47. }
    48. return output;
    49. }
    50. }