AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

RegEx Frage

Ein Thema von hansklok · begonnen am 18. Jan 2016 · letzter Beitrag vom 18. Mai 2016
Antwort Antwort
Seite 2 von 2     12   
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#11

AW: RegEx Frage

  Alt 4. Feb 2016, 20:12
Hast du Möglichkeit, das ganze nachzubearbeiten?

Also einfach die @s wegzudoktorn, wenn in der Gruppe davor eines der Signalwörter auftritt? Das sollte wesentlich einfacher gehen, als das Regex so anzupassen, dass die Signalwörter berücksichtigt werden und dann eine Fallunterscheidung zu machen.

Was sollte denn passieren bei:

Code:
1 NAME @Max /Mustermann/@
1 FAMC I123
1 FAMC @I123
1 FAMC I123@
1 FAMC @I@12@3@
Es gibt einen Standard und da ist eindeutig definiert, dass nach FAMS, FAMC und CHIL ein von @-Zeichen eingeschlossener Wert folgen muss.
  Mit Zitat antworten Zitat
SProske

Registriert seit: 16. Feb 2015
Ort: Halle/S.
116 Beiträge
 
Delphi 10.2 Tokyo Enterprise
 
#12

AW: RegEx Frage

  Alt 4. Feb 2016, 21:14
So langsam wird es kompliziert

Probier mal:

Code:
(0|[1-9][\d]*)\h+(?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@\h+)?)(?<Special>FAM[SC]|CHIL)?(?(Special)|(?<NoSpecial>\w+))(?(Special)\h+@([^\v]*|)@|\h*([^\v]*|))
Im Prinzip prüfe ich, ob es einen deiner Specialfälle gibt, dann wird der gematcht - sonst das normale Verfahren. Und das nochmal für den Inhalt. Leider erzeugt Delphi im Match-Objekt einige eigentlich nicht vorhandene Gruppen - ich wüsste auch nicht wie ich die wegkriege. Deswegen mal zur Auswertung ein kleines Konsolenprogramm.

Zum selber durchgucken: https://regex101.com/r/gL9pX8/3

Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, RegularExpressions;

const
  Sample = '0 @Reference@ Objekt' + sLineBreak +
           '1 NAME Max /Mustermann/' + sLineBreak +
           '2 DATE 22 APR 2016' + sLineBreak +
           '1 FAMC @I123@' + sLineBreak +
           '1 FAMS @I124@' + sLineBreak +
           '1 CHIL @I125@';
var
  Match: TMatch;
begin
  try
    Match := TRegEx.Match(Sample, '(0|[1-9][\d]*)\h+(?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@\h+)?)(?<Special>FAM[SC]|CHIL)?(?(Special)|(?<NoSpecial>\w+))(?(Special)\h+@([^\v]*|)@|\h*([^\v]*|))');
    while Match.Success do
    begin
      if Match.Groups.Count = 7 then
      begin
        WriteLn('Number:' + Match.Groups[1].Value);
        WriteLn('Reference:' + Match.Groups[2].Value);
        WriteLn('Object:' + Match.Groups[4].Value);
        WriteLn('Content:' + Match.Groups[6].Value);
      end else if Match.Groups.Count = 6 then
      begin
        WriteLn('Number:' + Match.Groups[1].Value);
        WriteLn('Reference:' + Match.Groups[2].Value);
        WriteLn('Object:' + Match.Groups[3].Value);
        WriteLn('Content:' + Match.Groups[5].Value);
      end else
        WriteLn('Das ist anders :(:' + Match.Groups[0].Value);
      WriteLn('');
      Match := Match.NextMatch;
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Sebastian
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#13

AW: RegEx Frage

  Alt 5. Feb 2016, 00:22
Wow, danke! Wenn es nicht kompliziert wäre, hätte ich die Frage eh nicht ins Forum geworfen

Im Prinzip erfüllt das Pattern ja seine Sache

Also nochmal zum Aufbau:
Code:
Level (Reference) Tag (Value)
Level = Zahl 0-99
Reference = von @-Zeichen umschlossener Wert
Tag = einzelnes Wort, wobei die Wörter FAMC FAMS und CHIL eine Auswirkung auf Value, die dann ebenfalls von @-Zeichen umgeben ist haben
Value = Rest der Zeile, abhängig vom Tag

Die eingeklammerten Werte sind optional, ABER ich hätte gern immer genau 4 Match-SubStrings, also auch NIL Matches sind erlaubt. Das macht es einfacher für mich die Daten weiterzuverarbeiten.

Das Pattern
Code:
(?x)
(0|[1-9][\d]*)\h+
(?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@\h+)?)
(?<Special>FAM[SC]|CHIL)?
(?(Special)|(?<NoSpecial>\w+))
(?(Special)\h+@([^\v]*|)@|\h*([^\v]*|))
hat aber bis zu 6 Match-SubStrings

Also sollte folgendes rauskommen:
Code:
1 = Level
2 = (Reference)
3 = Tag (Special/NoSpecial)
4 = Value
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#14

AW: RegEx Frage

  Alt 5. Feb 2016, 00:58
Wenn du einen Parser für eine Zeile schreiben würdest, hättest du zuverlässigere Ergebnisse und wärst wohl schon fertig.

Hier mal so ein Parser mit der stateless Bibliothek
Delphi-Quellcode:
unit Unit2;

interface

uses
  System.SysUtils,
  stateless;

type
  TRow = record
  public
    Level : Integer;
    Reference: string;
    Tag : string;
    Value : string;
    RefValue : string;
  public
    class function Parse( const AStr: string ): TRow; static;
    function ToString( ): string;
  end;

implementation

uses
  System.StrUtils;

class function TRow.Parse( const AStr: string ): TRow;
type
{$SCOPEDENUMS ON}
  TParserState = ( Level, ReferenceOrTag, Reference, TagStart, Tag, ValueOrRefValue, Value, RefValue, Finished, Error );
  TParseerTrigger = ( ParseChar, ParseFinish );
{$SCOPEDENUMS OFF}
  TRowParserSM = TStateMachine<TParserState, TParseerTrigger>;
var
  sm : TRowParserSM;
  pc : TRowParserSM.TTriggerWithParameters<Char>;
  buffer : string;
  c : Char;
  res : TRow;
  errorTransition: TRowParserSM.TTransition;
begin
  buffer := '';
  sm := TRowParserSM.Create( TParserState.Level );
  try
    pc := sm.SetTriggerParameters<Char>( TParseerTrigger.ParseChar );

{$REGION 'Configuration'}
    { Level }

    sm.Configure( TParserState.Level )
    {} .OnEntryFrom<Char>( pc,
      procedure( const c: Char; const t: TRowParserSM.TTransition )
      begin
        if t.IsReentry
        then
          buffer := buffer + c;
      end )
    {} .OnExit(
      procedure( const t: TRowParserSM.TTransition )
      begin
        if not t.IsReentry
        then
          begin
            res.Level := StrToInt( buffer );
            buffer := '';
          end;
      end )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Error )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          '0' .. '9':
            Result := TParserState.Level;
          ' ':
            begin
              if buffer.IsEmpty
              then
                Result := TParserState.Error
              else
                begin
                  Result := TParserState.ReferenceOrTag;
                end;
            end;
        else
          Result := TParserState.Error;
        end;
      end );

    { ReferenceOrTag }

    sm.Configure( TParserState.ReferenceOrTag )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Error )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          '@':
            Result := TParserState.Reference;
          ' ':
            Result := TParserState.ReferenceOrTag;
          'A' .. 'Z':
            Result := TParserState.Tag;
        else
          Result := TParserState.Error;
        end;
      end );

    { Reference }

    sm.Configure( TParserState.Reference )
    {} .OnEntryFrom<Char>( pc,
      procedure( const c: Char; const t: TRowParserSM.TTransition )
      begin
        if t.IsReentry
        then
          buffer := buffer + c;
      end )
    {} .OnExit(
      procedure( const t: TRowParserSM.TTransition )
      begin
        if not t.IsReentry
        then
          begin
            res.Reference := buffer;
            buffer := '';
          end;
      end )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Error )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          '@':
            if buffer.IsEmpty
            then
              Result := TParserState.Error
            else
              Result := TParserState.TagStart;
        else
          Result := TParserState.Reference;
        end;
      end );

    { TagStart }

    sm.Configure( TParserState.TagStart )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Error )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          ' ':
            Result := TParserState.TagStart;
          'A' .. 'Z':
            Result := TParserState.Tag;
        else
          Result := TParserState.Error;
        end;
      end );

    { Tag }

    sm.Configure( TParserState.Tag )
    {} .OnEntryFrom<Char>( pc,
      procedure( const c: Char )
      begin
        buffer := buffer + c;
      end )
    {} .OnExit(
      procedure( const t: TRowParserSM.TTransition )
      begin
        if not t.IsReentry
        then
          begin
            res.Tag := buffer;
            buffer := '';
          end;
      end )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Finished )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          'A' .. 'Z':
            if buffer.Length >= 4
            then
              Result := TParserState.Error
            else
              Result := TParserState.Tag;
          ' ':
            Result := TParserState.ValueOrRefValue;
        else
          Result := TParserState.Error;
        end;
      end );

    { ValueOrRefValue }

    sm.Configure( TParserState.ValueOrRefValue )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Finished )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          '@':
            Result := TParserState.RefValue;
          ' ':
            Result := TParserState.ValueOrRefValue;
        else
          Result := TParserState.Value;
        end;
      end );

    { Value }

    sm.Configure( TParserState.Value )
    {} .OnEntryFrom<Char>( pc,
      procedure( const c: Char; const t: TRowParserSM.TTransition )
      begin
        buffer := buffer + c;
      end )
    {} .OnExit(
      procedure( const t: TRowParserSM.TTransition )
      begin
        if not t.IsReentry
        then
          begin
            res.Value := buffer;
            buffer := '';
          end;
      end )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Finished )
    {} .PermitReentry( TParseerTrigger.ParseChar );

    { RefValue }

    sm.Configure( TParserState.RefValue )
    {} .OnEntryFrom<Char>( pc,
      procedure( const c: Char; const t: TRowParserSM.TTransition )
      begin
        if t.IsReentry
        then
          buffer := buffer + c;
      end )
    {} .OnExit(
      procedure( const t: TRowParserSM.TTransition )
      begin
        if not t.IsReentry
        then
          begin
            res.RefValue := buffer;
            buffer := '';
          end;
      end )
    {} .Permit( TParseerTrigger.ParseFinish, TParserState.Error )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          '@':
            if buffer.IsEmpty
            then
              Result := TParserState.Error
            else
              Result := TParserState.Finished;
        else
          Result := TParserState.RefValue;
        end;
      end );

    { Finished }

    sm.Configure( TParserState.Finished )
    {} .PermitReentry( TParseerTrigger.ParseFinish )
    {} .PermitDynamic<Char>( pc,
      function( const c: Char ): TParserState
      begin
        case c of
          ' ':
            Result := TParserState.Finished;
        else
          Result := TParserState.Error;
        end;
      end );

    { Error }

    sm.Configure( TParserState.Error )
    {} .OnEntry(
      procedure( const t: TRowParserSM.TTransition )
      begin
        errorTransition := t;
      end );
{$ENDREGION}
    { Parse the string }

    for c in AStr do
      begin
        if not sm.CanFire( TParseerTrigger.ParseChar )
        then
          break;
        sm.Fire<Char>( pc, c );
      end;

    { Fire Finish Trigger }

    if sm.CanFire( TParseerTrigger.ParseFinish )
    then
      sm.Fire( TParseerTrigger.ParseFinish );

    { Check the final state }

    if sm.State <> TParserState.Finished
    then
      raise Exception.Create( errorTransition.ToString );

    Result := res;

  finally
    sm.Free;
  end;
end;

function TRow.ToString: string;
begin
  Result := Level.ToString( )
  {} + IfThen( Reference.IsEmpty, '', ' @' + Reference + '@' )
  {} + ' ' + Tag.ToUpperInvariant( )
  {} + IfThen( Value.IsEmpty, '', ' ' + Value )
  {} + IfThen( RefValue.IsEmpty, '', ' @' + RefValue + '@' );
end;

end.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo ( 5. Feb 2016 um 09:57 Uhr)
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#15

AW: RegEx Frage

  Alt 8. Mai 2016, 15:40
So, ich greife das Thema erneut auf. Ich habe nun ein wunderbar funktionierendes RegEx-Pattern:
Code:
(0|[1-9][\d]*) (?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@ )?)([A-Za-z0-9_]+)( [^\n\r]*|)
Nun gibt es drei Ausnahmen, die ich gern integrieren würde. Sie betreffen die Teil-Abschnitte
Code:
([A-Za-z0-9_]+)
und
Code:
( [^\n\r]*|)
. Wenn
Code:
([A-Za-z0-9_]+)
<> NOTE, CONT oder CONC ist, soll
Code:
( [^\n\r]*|)
getrimmt werden, sonst nicht. Wie würde das aussehen? Ich kenne mich mit Konditionalen Ausdrücken leider gar nicht aus.

Vielen Dank im Voraus
  Mit Zitat antworten Zitat
idefix2

Registriert seit: 17. Mär 2010
Ort: Wien
1.027 Beiträge
 
RAD-Studio 2009 Pro
 
#16

AW: RegEx Frage

  Alt 17. Mai 2016, 10:50
Was meinst du mit "soll getrimmt werden"?

Prinzipiell kannst du die drei Sonderfälle NOTE, CONT oder CONC behandeln, in dem du etwas auf die Art:

Code:
(((NOTE|CONT|CONC)Sonderfall)|(([A-Za-z0-9_]+)Normalfall))
machst - das würde zwar auch den String "NOTENormalfall" parsen, ich glaube allerdings auf Grund der Angaben, das das Problem in deinem Fall nicht relevant ist.

Geändert von idefix2 (17. Mai 2016 um 10:55 Uhr)
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#17

AW: RegEx Frage

  Alt 17. Mai 2016, 13:33
Also, es geht darum, dass bei den drei genannten Fällen, das gematchte Ergebnis nicht getrimmt werden soll, in allen anderen Fällen schon. Ich meine also, dass alle Leerzeichen vor und nach dem Match automatisch entfernt werden. Ist das mittels RegEx möglich? Das würde mir erheblich Rechenzeit Sören, dennbisher prüfe ich dann jedes Match und führe ggf. die interne String-Trimfunktion aus. Das kostet Zeit, vor allem bei sehr langen Strings.
  Mit Zitat antworten Zitat
idefix2

Registriert seit: 17. Mär 2010
Ort: Wien
1.027 Beiträge
 
RAD-Studio 2009 Pro
 
#18

AW: RegEx Frage

  Alt 18. Mai 2016, 08:34
Naja, prinzipiell kannst du es so machen, wie ich eben beschrieben habe. Also eine Alternative zwischen "Sonderfall" und "Normalfall", wobei du im Normalfall trimmst und in den Sonderfällen nicht. Klar ist, dass du dann das Ergebnis in zwei verschiedenen Variablen erhältst, und je nachdem die eine oder die andere Variable verwenden musst. Ob aber diese Vorgangsweise insgesamt schneller ist als ein nachträgliches Trim, halte ich für sehr fraglich, die Rechenzeit für die Regex-Auswertung steigt möglicherweise stärker an als um die Zeit, die ein Trim kostet.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:44 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz