Autor |
Beitrag |
zongo-joe
Beiträge: 134
win xp prof
D3, D4, D7
|
Verfasst: Sa 02.02.19 17:52
Tag zusammen, ich muss meine Kontoauszüge parsen, dazu habe ich einen kleinen Parser geschrieben, der aus Strings Euro-Beträge extrahieren kann; soweit ich das probiert habe, kommt er mit sehr vielen versteckten Tücken der Kontoauszüge zurecht.
Ist zwar etwas krude gecodet, aber funzt.
Hoffe er gefällt Euch. Für Verbesserungsvorschläge bin ich natürlich offen:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135:
| function eurosuchen(x:string;trenner:char;erster,meldung,negativ,zweinachkomma,runden:boolean):string; const waehrung='€'; var i,pos1,pos2 : integer; rbetrag:string; gefund:boolean; hlp:char;
function betrag(x:string; posi:integer; trenner:char):string; const meldung=true; var i,pos2,pos3:integer; res:string; begin res:='';
dec(posi); while (posi > 0) and (x[posi] in [' ','0'..'9','-',trenner]) do begin res:= x[posi] + res; dec(posi); end;
while (length(res) > 0) and (res[length(res)] in [' ',trenner,'-']) do begin res := copy(res,1,length(res)-1); end;
while (length(res) > 0) and (res[1] in [' ',trenner]) do begin res := copy(res,2,length(res)-1); end;
pos1:=0; for i:=1 to length(res) do if res[i]=' ' then pos1:=i; if pos1>0 then res := copy(res,pos1+1,length(res)-pos1);
repeat i:=length(res); pos2:=0; for i:=length(res) downto 1 do if res[i]=trenner then begin inc(pos2); pos3:=i; end; if pos2>1 then res:=copy(res,pos3+1,length(res)-pos3); until pos2<2;
repeat i:=length(res); pos2:=0; for i:=length(res) downto 1 do if res[i]='-' then begin inc(pos2); pos3:=i; end; if pos2>1 then res:=copy(res,pos3+1,length(res)-pos3); until pos2<2;
if pos('-',res) > 1 then res:=copy(res,pos('-',res),length(res)-pos('-',res)+1);
result:=res;
end;
begin rbetrag:=''; gefund:=false;
if length(x) > 0 then begin
if erster then begin i:=1; while (not gefund) and (i<=length(x)) do begin if x[i]=waehrung then begin rbetrag:=betrag(x,i,trenner); if rbetrag<>'' then gefund:=true; end; inc(i); end;
end else begin i:=length(x); while (not gefund) and (i>0) do begin if x[i]=waehrung then begin rbetrag:=betrag(x,i,trenner); if rbetrag<>'' then gefund:=true; end; dec(i); end;
end; if gefund and (not negativ) and (rbetrag[1]='-') then rbetrag:=copy(rbetrag,2,length(rbetrag)-1);
if zweinachkomma and (pos(trenner,rbetrag)>0) and (pos(trenner,rbetrag) < length(rbetrag)-2) then begin if runden then begin hlp:=rbetrag[pos(trenner,rbetrag)+3]; if hlp in ['5','6','7','8','9'] then rbetrag:=copy(rbetrag,1,pos(trenner,rbetrag)+1) + intTostr(strToInt(copy(rbetrag,pos(trenner,rbetrag)+2,1))+1) else rbetrag := copy(rbetrag,1,pos(trenner,rbetrag)+2);
end else begin rbetrag := copy(rbetrag,1,pos(trenner,rbetrag)+2);
end; end;
end; if (not gefund) and meldung then showmessage('Keinen gültigen Betrag in '+x+' gefunden');
result:=rbetrag;
end; |
Aufruf: zB:
showmessage(eurosuchen('Hans Dampf, Geld für Bestellnummer 4567.44, 314,14€',',',true,true,true,true,true));
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 02.02.19 18:02
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
zongo-joe
Beiträge: 134
win xp prof
D3, D4, D7
|
Verfasst: Sa 02.02.19 18:10
Frühlingsrolle hat folgendes geschrieben : | Guten Abend
bevor du noch weitere Themen veröffentlichst:
Schau' dir mal die Funktion Pos() an. |
Warum ? die nützt hier nicht viel, weil sie imho nur die erste position im String zurückgibt..
Wenn Du damit nach '€' suchen willst und dann mal eben schnell die Zahlen davor abgeifen möchtest:
wie siehts dann hier aus:
Bestellnummer 12345 34,67€
Bestellnummer 12345,34,67€
Bestellnummer 12345 34,67 €
Bestellnummer 12345 -34,67€
das Problem ist etwas komplexer, darum der Code.
|
|
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 02.02.19 18:42
- Nachträglich durch die Entwickler-Ecke gelöscht -
|
|
jaenicke
Beiträge: 19283
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 03.02.19 07:11
Was ist denn das für eine Bank, die keinen digitalen Export anbietet?
Bei der Postbank bekommt man z.B. auch XML, bei der 1822direkt CSV, ...
Jedenfalls würde ich an der Stelle am besten mit Regular Expressions arbeiten, denn da ist doch etwas Flexibilität erforderlich. Für die genannten Fälle würde es damit gehen:
Quelltext 1:
| ([a-zA-ZäöüÄÖÜ0-9\.\,\ ]{1,})[\ ]{1,}([0-9\.]*)[\,\ ]{1,}([\-0-9\ ]*\,[0-9\€\ ]*) |
Man könnte das auch kürzer schreiben, aber so ist es denke ich verständlichsten:
Immer die Elemente in runden Klammern werden als Ergebnisgruppe erfasst.
- In der ersten Gruppe also Buchstaben, Zahlen, Punkt, Komma und Leerzeichen, davon mindestens eins, maximal unbegrenzt
- Dazwischen mindestens ein Leerzeichen
- In der zweiten Gruppe nur Zahlen oder Punkte mit unbegrenzter Anzahl
- Dazwischen mindestens ein Komma oder Leerzeichen
- In der dritten Gruppe Minuszeichen, Zahlen und Leerzeichen, dann ein Komma, dann noch einmal Zahlen, Leerzeichen oder Eurozeichen mit unbegrenzter Anzahl
Hier im Online-Tester:
Und dazu der Delphi-Quelltext als kurzes Beispiel (kompilierbar in der aktuellen kostenlosen Community Edition):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| uses System.RegularExpressions, Vcl.Dialogs;
procedure Test(const AText: string); var RegEx: TRegEx; Match: TMatch; begin RegEx := TRegEx.Create('([a-zA-ZäöüÄÖÜ0-9\.\,\ ]{1,})[\ ]{1,}([0-9\.]*)[\,\ ]{1,}([\-0-9\ ]*\,[0-9\€\ ]*)'); Match := RegEx.Match(AText); if Match.Success then ShowMessage(AText + ' ergibt:' + sLineBreak + Match.Groups[1].Value + sLineBreak + Match.Groups[2].Value + sLineBreak + Match.Groups[3].Value) else ShowMessage(AText + ': keine Übereinstimmung!'); end;
Test('Hans Dampf, Geld für Bestellnummer 4567.44, 314,14€'); Test('Bestellnummer 12345 34,67€'); Test('Bestellnummer 12345,34,67€'); Test('Bestellnummer 12345 34,67 €'); Test('Bestellnummer 12345 -34,67€'); |
Das gibt aus:
(Und so weiter)
Einloggen, um Attachments anzusehen!
Für diesen Beitrag haben gedankt: Narses
|
|
Sinspin
Beiträge: 1326
Erhaltene Danke: 117
Win 10
RIO, CE, Lazarus
|
Verfasst: So 03.02.19 13:39
jaenicke hat folgendes geschrieben : | Was ist denn das für eine Bank, die keinen digitalen Export anbietet? |
Interessanter finde ich die Frage warum Banken heutzutage alle ihr eigenes Süppchen kochen und jede Bank was anderes liefert.
Zumindest MT940 würde ich von jeder Bank erwarten und CSV was zumindest die gleichen Felder enthält.
Ich habe auch schon xls Exporte gesehen oder Tabellen mit nur drei Feldern.
Faszinierend ist für mich die Frage, wenn da keine fortlaufende Buchungsnummer an den Datensätzen ist, haben die intern eine? Und was machen die bei Rückfragen oder Fehlbuchungen?
Ich denke jeder Entwickler hat irgendwo eine Sammlung von Routinen um Text zu zerpflücken.
Aber ich würde mir garantiert keine Routine mit zig Parametern schreiben bei der gerade das Währungszeichen (übrigens können das bis zu drei Zeichen sein) nicht übergeben werden kann.
Und Sowas wie "Runden" geht bei Währung schonmal garnicht. Per definition hat Währung 4 Nachkommastellen und es gibt dafür einen Datentyp : Currency.
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
|
|
jaenicke
Beiträge: 19283
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 03.02.19 20:36
Ach ja, eins noch:
Es ist hilfreich, wenn eine Bestellnummer ein bestimmtes Format hat. Zum Beispiel mit einem Präfix wie AB oder auch 121 am Anfang und eine bestimmte Länge. Dann lässt sich die Bestellnummer viel einfacher per RegEx herausfiltern und wird auch gefunden egal was der Kunde sonst noch schreibt.
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Mo 04.02.19 09:28
Banken sind so ein besonderes Thema.
CSV ist im privaten Bereich durchaus ein Angebot in den Bank-Portalen. Dass dabei völlig wilde Zusammensetzungen existieren, bleibt nicht aus, da es keinem Banken-Standard unterrworfen ist.
Standard-Formate im Bankenbereich:
MT940 gibt es auch und kann derzeit durchaus noch als Standard betrachtet werden. Allerdings wird es von einigen Banken für den privaten Endkunden nur gegen bares zur Verfügung gestellt, da das Format eigentlich für die Kommunikation zwischen Geldinstituten vorgesehen war/ist.
CAMT (53 für Kontoauszüge) ist quasi ein "Nachfolger" von MT940. Es gibt durchaus Banken, die die MT-Formate bereits verweigern (wollen), sofern sie CAMT schon liefern können. Dass liegt daran, dass CAMT sein einigen Jahren ein verpflichtendes Format für Banken ist.
UNIFI soll MT-Format ablösen. Dabei handelt es sich um XML-Formate. Meines Wissens ist die Definition aber noch nicht abgeschlossen. Das mag vielleicht daran liegen, dass die Definition durch ein ISO-Konsortium definiert wird, bei dem die Banken angeblich nicht vernünftig eingebunden wurden/werden.
Fazit:
Es gibt definierte Formate und man muss keine Texte parsen, wenn man wirklich nur den Buchungssatz benötigt.
Problematisch sind z.B. eher Buchungen, die als Sammelbuchung gelten. Bei denen werden im Buchungstext dann die Einzelbuchungen aufgeführt. Diese unterligen keinem definierten Format, wodurch die automatisierten Zuordnungen erheblich erschwert werden.
Woher ich das alles weiß?
Ich bin Entwickler bei einer Bank und habe auch mit diesen Schnittstellen zu kämpfen. Besonders dann, wenn eine Bank mal wieder meint, klar definierte Feldinhalte zu ignorieren.
Auch wir müssen die Texte analysieren, um automatische Zuordnungen zu machen. Es bleibt dabei immer ein gewisser Prozentsatz an manuellen Prüfungen übrig.
|
|
|