![]() |
AW: RegEx Frage
Zitat:
|
AW: RegEx Frage
So langsam wird es kompliziert :)
Probier mal:
Code:
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.
(0|[1-9][\d]*)\h+(?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@\h+)?)(?<Special>FAM[SC]|CHIL)?(?(Special)|(?<NoSpecial>\w+))(?(Special)\h+@([^\v]*|)@|\h*([^\v]*|))
Zum selber durchgucken: ![]()
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. |
AW: RegEx Frage
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 :D Also nochmal zum Aufbau:
Code:
Level = Zahl 0-99
Level (Reference) Tag (Value)
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:
hat aber bis zu 6 Match-SubStrings
(?x)
(0|[1-9][\d]*)\h+ (?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@\h+)?) (?<Special>FAM[SC]|CHIL)? (?(Special)|(?<NoSpecial>\w+)) (?(Special)\h+@([^\v]*|)@|\h*([^\v]*|)) Also sollte folgendes rauskommen:
Code:
1 = Level
2 = (Reference) 3 = Tag (Special/NoSpecial) 4 = Value |
AW: RegEx Frage
Wenn du einen Parser für eine Zeile schreiben würdest, hättest du zuverlässigere Ergebnisse und wärst wohl schon fertig. :stupid:
Hier mal so ein Parser mit der ![]()
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. |
AW: RegEx Frage
So, ich greife das Thema erneut auf. Ich habe nun ein wunderbar funktionierendes RegEx-Pattern:
Code:
Nun gibt es drei Ausnahmen, die ich gern integrieren würde. Sie betreffen die Teil-Abschnitte
(0|[1-9][\d]*) (?:@?((?<=@)[^@]+(?=@)|(?!@))(?:@ )?)([A-Za-z0-9_]+)( [^\n\r]*|)
Code:
und
([A-Za-z0-9_]+)
Code:
. Wenn
( [^\n\r]*|)
Code:
<> NOTE, CONT oder CONC ist, soll
([A-Za-z0-9_]+)
Code:
getrimmt werden, sonst nicht. Wie würde das aussehen? Ich kenne mich mit Konditionalen Ausdrücken leider gar nicht aus.
( [^\n\r]*|)
Vielen Dank im Voraus |
AW: RegEx Frage
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:
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.
(((NOTE|CONT|CONC)Sonderfall)|(([A-Za-z0-9_]+)Normalfall))
|
AW: RegEx Frage
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.
|
AW: RegEx Frage
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.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:31 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz