Autor Beitrag
Ronald
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Do 03.07.14 09:48 
Hallo Zusammen,

ich brauch mal Hilfe. Ich arbeite als Controller bei einem großen Handelshaus.
Dort werden in BW artikelgenaue Daten für unser MIS zu Warengruppen aggregiert ausgelesen, die wir in unser MIS importieren. Jetzt haben wir beginnend mit 2014 die Rechnungsgrundlage für die Margenermittlung umgestellt. Dies läßt sich im BW auf artikelebene nicht nachbauen, aber in unserem MIS geht das. Die Daten bekomme ich als sehr große Flat-Files (Die Datei kann nicht mal mit Word geöffnet werden).
Jetzt muss ich aber für die Historie die Umbewertungen nachrechnen und zusätzlich in das MIS laden, so dass wir wenigsten hier einen sinnvollen Vorjahresvergleich bei den Margen abbilden können. Ist ja wichtig für die Unternehmenssteuerung. Das heißt: Die großen Flatfiles einlesen, die umzubewertenden Kostenarten ermitteln und die Korrekturwerte in eine neuen Flatfile schreiben. Die Dateien sind leider in Summe so groß, dass dies nicht komplett im Arbeitsspeicher geht. Also habe ich den so ungeliebten langsamen File-IO verwendet. Läuft ewig, aber funktioniert. Leider ist dieser ganze Prozess maximal Zeitkritisch und ich suche daher nach Optimierungpotential in der Laufzeit (aktuell ca 4-6 Stunden). Und diesen Prozess müssen wir jedesmal von neuem durchführen, wenn die Warenfachhierarchie von den verantwortlichen geändert wird.
Ich suche nach einer Lösung, die mir Laufzeit einspart. Leider kommt die Verarbeitung der kompletten Datei im Arbeitsspeicher nicht in Frage. Ein Aufsplitten der Datei in mehrere Kleinere bringt von der Laufzeit durch vorgelagerte Prozesse auch keinen Performancevorteil. Ich hab das Programm mal angehängt
Die Daten liegen mir als CSV-Datei vor:

414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97
414;1231;A;2;AOGLD;I;6;2012;Eur;137,62
414;1291;A;16;EVVKWIHW;I;5;2012;Eur;0,19
414;1291;A;16;EVGLDHW;I;5;2012;Eur;0,15
414;1231;B;2;AOVKWI;I;6;2012;Eur;228,63
414;1231;B;2;AOVKWO;I;6;2012;Eur;213,66
414;1231;B;2;AOGLD;I;6;2012;Eur;141,05
414;1231;E;2;AOVKWI;I;6;2012;Eur;19,90
414;1231;E;2;AOVKWO;I;6;2012;Eur;18,60
414;1231;E;2;AOGLD;I;6;2012;Eur;13,34
414;1231;M;2;AOVKWI;I;6;2012;Eur;1117,60
414;1231;M;2;AOVKWO;I;6;2012;Eur;1044,50
414;1231;M;2;AOGLD;I;6;2012;Eur;687,52
414;1231;P;2;AOVKWI;I;6;2012;Eur;104,76
Einloggen, um Attachments anzusehen!
chrisw
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 439
Erhaltene Danke: 3

W2K
D7
BeitragVerfasst: Do 03.07.14 11:04 
Spricht etwas dagegen die csv-Datei in eine Datenbank (MYSQL, PostgreSQL, ...) zu importieren, denke dann kannst du jegliche SQL Abfrage in angebrachter Zeit starten.

ein Beispiel für einen Import zu MYSQL
ausblenden SQL-Anweisung
1:
2:
3:
4:
5:
6:
7:
8:
LOAD DATA LOCAL INFILE 'csvTest.csv'
INTO TABLE csvImport
FIELDS TERMINATED BY ';'
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(field1, @dummy, field2, @rechenVar)
SET field3 = @rechenVar*10


Christian

_________________
Man sollte keine Dummheit zweimal begehen, die Auswahl ist schließlich groß genug.
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Do 03.07.14 11:04 
Über welche Dateigrößen reden wir hier ? Irgendwann ist jede Textdatei langsam, sie muß nur groß genug sein ;)

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Ronald Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Do 03.07.14 11:56 
Also die Dateien sehen so aus

Dateigröße: 3,6 GB zum Beispiel. Liegen alle so in diesem Korridor

Das mit der Datenbank probier ich mal. War gerade bei der IT. Die richten mir eine Tabelle ein, auf die ich vollzugriff habe. Mal sehen, ob ich meine Ziele über einen Select erreiche. Andernfall muss ich mal sehen, wie ich die Daten connecte. Hab das schon ewig nicht mehr gemacht.
Bin halt so ein Problemlöser-Entwickler für Nischen im Controlling für Sachen, die mir die IT nicht liefern kann, wie ich sie brauche. Kommt halt eher selten vor, aber wenn dann wichtig ^^

_________________
Performance ist durch nicht zu ersetzen außer durch Performance
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Do 03.07.14 12:36 
Also um die 4GB. Da wundert mich schon allerhand nicht mehr ;)

Sämtliche Delphi-Bordmittel wie TStringList oder TMemo sind mit allem, was mehr als ~20k Zeilen (!) enthält, überfordert. Solche Files kann man ggf. noch mit einem Compiler-Editor a la Visual C# bearbeiten - aber bei 32Bit und deinen Dateigrößen ist auch das unmöglich (max 3GB Speicher allozierbar).

Ich würde die Datei zeilenweise einlesen und verarbeiten:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
var
   f: TextFile;
   TheLine: string;
begin
   AssignFile(f,'TheFile.CSV');
   Reset(f);
   while not eof(f) do
   begin
      ReadLn(f,TheLine);
      //Processing here
   end;
   CloseFile(f);
end;


Eine andere Möglichkeit sehe ich nicht, um das ganze in beherrschbaren Zeiträumen zu halten.

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Do 03.07.14 12:38 
Schmeiß mal deine Progressbar raus. Die aktualisiert bei jeder gelesenen Zeile. Das kostet bei dieser Dateigröße richtig Zeit.
Und wenn du die unbedingt brauchst, dann aktualisiere nur bei jeder 1000sten Zeile. Ich hatte schon Befürchtungen, dass da irgendwo ein Application.ProcessMessages steht. Ein absoluter Performance-Killer.

Wobei ich denke, dass das Bulk-Insert in die Datenbank trotzdem die bessere Lösung sein wird.

@user profile iconOlafSt:
Schau dir doch erstmal den Source an. Das wird schon so gemacht.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 03.07.14 13:12 
Der größte Performancekiller ist dort das Readln. Das ist nämlich erst einmal sehr langsam. Sinnvoller ist die Verwendung von MMFs, die genau für so große Dateien ideal sind. Eine entsprechende Kapselung findest du von mir hier:
www.entwickler-ecke....+Reader_99933,0.html

Damit ist dann auch gleich die äußerst ungünstig implementierte Fortschrittsanzeige erledigt, denn Position und Größe bekommst du dort neben einem zeilenweisen Auslesen direkt...
Dazu kommt dann noch wie schon erwähnt wurde, dass die Fortschrittsanzeige nicht bei jedem Durchlauf aktualisiert werden sollte.

Am besten wäre das ganze direkt in einen Thread zu packen. Dann kannst du die aktuelle Position mit Hilfe einer atomaren Funktion (AtomicExchange, ...) in ein von außen erreichbares Feld schreiben und das nur periodisch aktualisieren.

Mehr kann ich aktuell nicht zeigen, da ich das unterwegs am Handy schreibe.

Für diesen Beitrag haben gedankt: Martok
Ronald Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Do 03.07.14 13:21 
Ok, das mit dem Progressbar hatt ich gar nicht so auf dem Schirm.
Ich setz mal bei Euren Vorschlägen an und probier das mal aus.
Vielen Dank

_________________
Performance ist durch nicht zu ersetzen außer durch Performance
Ronald Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Do 03.07.14 15:40 
Also, der relevante Datenbestand eines Jahres ist jetzt in einer Tabelle des MSSQL-Servers.
Ca. 97 Mio Datensätze :)

_________________
Performance ist durch nicht zu ersetzen außer durch Performance
Teekeks
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 211
Erhaltene Danke: 23



BeitragVerfasst: Do 03.07.14 20:05 
MSSQL ist so ziemlich das schlimmste was du machen kannst bei vielen Datensätzen.
Nach den Tests die ich gemacht habe, würde ich Firebird nutzen, das verliert fast nix an Geschwindigkeit bei seeeehr vielen Datensätzen (auch wenn es nicht soo schnell ist bei wenigen Daten)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 03.07.14 20:34 
Wobei es da auch noch massive Unterschiede zwischen den Zugriffsbibliotheken gibt. ZEOS war z.B. bei MS SQL in meinen Tests massiv langsamer als FireDAC, insbesondere bei großen Tabellen, bei Firebird war der Unterschied z.B. deutlich kleiner.
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Fr 04.07.14 21:52 
Hallo Alle,

das Problem von Ronald ist doch, dass es eine einmalige Aktion ist, so wie ich seine Frage verstanden habe. Da darf man doch nicht viel Arbeit in Datenbanken usw. stecken.

Ich hatte letzten Messwertreihen nach bestimmten sehr kurzzeitigen Ereignissen zu durchsuchen und die Messwerte statistisch auszuwerten. Die Daten habe ich als CSV bekommen, waren etwa 100 GByte.
Die Daten lagen in Einzeldateien fortlaufend numeriert mit einer Größe von je einigen 100 MB vor. Diese Dateien habe ich zunächst in Binärdaten gewandelt (das dauerte ein paar Stunden, aber es sparte für die folgende Auswertung das dauernde Parsen der CSV. (Interessanterweise waren die Binärdateien größer als die CSV-Dateien, was an der Art der Daten liegt.) Ab dann dauerte jede Auswertung nur wenige Sekunden, selbst ein Plott über die gesamte Zeit war in wenigen Minuten gemacht. (PC: Single Core 2,2 GHz, also nicht ganz neu).

Ich würde also:
a) Dateien splitten z. B. in 500 MB-Pakete
b) Die Datei in Binärdaten wandeln
c) Die folgenden Zuordnung der Artikel zu Warengruppen einfach sequentiell abarbeiten.

MySQL hatte ich für diese Aufgabe auch untersucht, ging sehr gut, Performance für sehr große Datenmengen habe ich nicht getestet.

Vielleicht hilft das ja,
Gruß
GuaAck
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 05.07.14 05:47 
user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
das Problem von Ronald ist doch, dass es eine einmalige Aktion ist, so wie ich seine Frage verstanden habe. Da darf man doch nicht viel Arbeit in Datenbanken usw. stecken.
Das hast du glaube ich falsch verstanden:
user profile iconRonald hat folgendes geschrieben Zum zitierten Posting springen:
Leider ist dieser ganze Prozess maximal Zeitkritisch und ich suche daher nach Optimierungpotential in der Laufzeit (aktuell ca 4-6 Stunden). Und diesen Prozess müssen wir jedesmal von neuem durchführen, wenn die Warenfachhierarchie von den verantwortlichen geändert wird.


user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
Diese Dateien habe ich zunächst in Binärdaten gewandelt (das dauerte ein paar Stunden, aber es sparte für die folgende Auswertung das dauernde Parsen der CSV. (Interessanterweise waren die Binärdateien größer als die CSV-Dateien, was an der Art der Daten liegt.)
Das dürfte daran liegen, dass in den Binärdaten die Daten mit einer festen Breite vorlagen. Das beschleunigt den Zugriff natürlich enorm und war auch der Weg, den alte Datenbanken wie DBase gegangen sind, aber die Größe wird je nach Art der Daten ggf. deutlich erhöht.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: So 06.07.14 00:37 
Hallo,

ich bin mir nicht sicher, ob Ronalds Version nicht sehr umständlich vorgeht.
Felder werden zum Teil nicht als Referenz übergeben, das ist natürlich viel unnötige Arbeit.
Auch das Case Kontrukt besteht wohl aus Copy&Paste. Freepascal konnte es aber gar nicht umsetzen, (EDIT) aber unter Lazarus funktioniert es.
Die Datei wurde zuerst vollständig eingelesen, um die Zeilen zu zählen???
Jeder Wert wurde viermal geparst,Statt einmal eine Zeile und dann kopieren.
Das wichtigste ist aber die Ein/Ausgabe:
Die Erhöhung des Pufferspeichers ( hier 32 KB ( 64 geht auch, ist aber nicht schneller ) ) hilft enorm:
EDIT:
FloatToStr ist langsam FloatToStrF wesentlcih schneller ( rundet selbst ) und MyFloatToStr4 noch etwas.
Dabei werden alle möglichen Nachkommazahlen vorab gespeichert.
real 0m21.609s sind jetzt 276000 Zeilen/s ( statt 188000/s ) Eingabe und 830000 Zeilen/s Ausgabe
ausblenden volle Höhe 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:
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:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:
385:
386:
387:
388:
389:
390:
391:
392:
393:
394:
395:
396:
397:
398:
399:
400:
401:
402:
403:
404:
405:
406:
407:
408:
409:
410:
411:
412:
413:
414:
415:
416:
417:
418:
419:
420:
421:
422:
423:
424:
425:
426:
427:
428:
429:
430:
431:
432:
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449:
450:
451:
452:
453:
454:
455:
456:
457:
458:
459:
460:
461:
462:
463:
464:
465:
466:
467:
468:
469:
470:
471:
472:
473:
474:
475:
476:
477:
478:
479:
480:
481:
482:
483:
484:
485:
486:
487:
488:
489:
490:
491:
492:
493:
494:
495:
496:
497:
498:
499:
500:
501:
502:
program unbenannt;
{$IFNDEF FPC}
   {$APPTYPE CONSOLE}
{$ENDIF}

(*
414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97
414;1231;A;2;AOGLD;I;6;2012;Eur;137,62
414;1291;A;16;EVVKWIHW;I;5;2012;Eur;0,19
414;1291;A;16;EVGLDHW;I;5;2012;Eur;0,15
414;1231;B;2;AOVKWI;I;6;2012;Eur;228,63
414;1231;B;2;AOVKWO;I;6;2012;Eur;213,66
414;1231;B;2;AOGLD;I;6;2012;Eur;141,05
414;1231;E;2;AOVKWI;I;6;2012;Eur;19,90
414;1231;E;2;AOVKWO;I;6;2012;Eur;18,60
414;1231;E;2;AOGLD;I;6;2012;Eur;13,34
414;1231;M;2;AOVKWI;I;6;2012;Eur;1117,60
414;1231;M;2;AOVKWO;I;6;2012;Eur;1044,50
*)


uses
  sysutils,classes,crt;
const
  ZehnHoch4   :double =   10000.0;
  RezZehnHoch4:double = 1/10000.0;

  maxStringCnt = 5;
  decSep = ',';
type
  tNachkomma4 = array[0..4of char;
  tArrNachkomma = array[0..9999of tNachkomma4;
  tMyString = string;
  tpChar = ^char; // oder Wchar und widestring
  tarr = Array[1..10of tMyString;
  tStringsIdx = 1..maxStringCnt;
const
  VergleichsStrings : array[tStringsIdx] of tMyString =
                       ('AOVKWO','AOGLD','EVVKWIHW','EVGLDHW','AOVKWI');
  ErsatzLogistrings : array[tStringsIdx] of tMyString =
                       ('Logi01','Logi02','Logi03','Logi04','Logi05');
  ErsatzVewastrings : array[tStringsIdx] of tMyString =
                       ('Vewa01','Vewa02','Vewa03','Vewa04','Vewa05');

  FaktorNetto    : double = 1.047;
  FaktorLogistik : double = 0.0375;
var
  Nachkomma :tArrNachkomma;
  dFaktorNetto : double;
  dFaktorLogistik: double;
  fIn, fOut : TextFile;
  sPath : string;

function ParseSem(var S: tMyString): tArr;
const
  delimiterChar = ';';
var
  pC : tpChar;
  aktPos,letztPos,k: Integer;
 begin
   pC := @S[1];
   dec(pC);// pC ist Null-basiert

   aktPos := 1;
   letztPos :=1;
   k := 1;
   repeat
     if pC[Aktpos] = delimiterChar then
     begin
       result[k]:=copy(S,letztPos,Aktpos-letztPos);
       LetztPos := Aktpos+1;
       inc(k);
     end;
     inc(Aktpos);
   until Aktpos>= length(S);
   result[k]:=copy(S,letztPos,Aktpos-letztPos+1);
end;


function RundeAuf4Stellen(zahl: double): double;
const
  ZehnHoch4   :double =   10000.0;
  RezZehnHoch4:double = 1/10000.0;
begin
  result := round(zahl*ZehnHoch4)*RezZehnHoch4;
end;

function MyFloatToStr4(d : double):string;
var
  len,i : integer;
begin
  d := round(d*ZehnHoch4)*RezZehnHoch4;
  i := trunc(d);
  result := IntToStr(i);
  len := length(result)+1;
  setlength(result,len+4);
  i := trunc(abs(d-i)*ZehnHoch4+0.5);
  Move(NachKomma[i],result[len],5);
end;

function CreateOutputS(var arrOut: tarr):tMyString;
var
  i: integer;
begin
  result :=  '';
  for i := 1 to 10 do
     result := result+ arrOut[i] + ';';
  setlength(result,Length(result)-1);
end;

Procedure ConcatAusgabe(var s: tMyString);
begin
  writeln(fOut,s);
end;

procedure SchreibeArray(var arrOut: tarr);
var
  sOut: tMyString;
begin
   sOut:=CreateOutputS(arrOut);
   ConcatAusgabe(sOut);
end;

function CheckVergleich(var s:tMyString): integer;
var
  i : integer;
begin
  result := 0;
  For i := Low(tStringsIdx) to High(tStringsIdx) do
  begin
    IF s = VergleichsStrings[i] then
    begin
      result := i;
      Break;
    end;
  end;
end;

procedure Button1Click;
var
  dWert, dLogistik, dVerwaltung, dNetto, dAufschlag : Double;

  arrSource : tarr;
  arrNew : tarr;
  arrLogistik : tarr;
  arrVerwaltung : tarr;
  s, fName : tMyString;
  InBuf,
  OutBuf : array of Char;
  i,rowcnt: integer;

  procedure SchreibeArrs(i: integer);
  begin
    IF i in [Low(VergleichsStrings)..High(VergleichsStrings)] then
    begin
      arrVerwaltung[5] := ErsatzVewastrings[i];
      arrLogistik[5]   := ErsatzLogistrings[i];
      SchreibeArray(arrNew);
      SchreibeArray(arrVerwaltung);
      SchreibeArray(arrLogistik);
    end;
  end;

begin
//???
  dFaktorNetto := FaktorNetto;
  dFaktorLogistik := FaktorLogistik;

      fName := 'Test.csv';
      assignfile(fIn,fName);
      reset(fIn);
      fName := 'Ausgabe.csv';
      AssignFile(fOut,fName);
      setlength(InBuf,1 shl 15);
      SetTextBuf(fIn,InBuf[0],length(InBuf));


      rewrite(fOut);
      setlength(OutBuf,1 shl 15);
      SetTextBuf(fOut,OutBuf[0],length(OutBuf));
      reset(fIn);

      rowcnt := 0;
      while not eof(fIn) do
      begin
        readln(fIn,s);

        // parsen
        arrSource:= parseSem(s);
        arrNew  :=arrSource;
        arrLogistik  :=arrSource;
        arrVerwaltung  :=arrSource;

        //Rechnen
        dWert := StrToFloat(arrSource[10]);
        // Werte umrechnen
        dNetto := dWert / dFaktorNetto;
        dAufschlag := dWert - dNetto;
        dLogistik := dNetto * dFaktorLogistik;
        dVerwaltung := dAufschlag - dLogistik;
        dWert := dNetto + dLogistik;
//        arrLogistik[10]   := FloatToStr(- round(dLogistik*ZehnHoch4)*RezZehnHoch4);
//        arrLogistik[10]   := FloatToStrF(-dLogistik,ffFixed,0,4);//MyFloatToStr4(-dLogistik);
        arrLogistik[10]   :=  MyFloatToStr4(-dLogistik);
//        arrVerwaltung[10] := FloatToStr(- round(dVerwaltung*ZehnHoch4)*RezZehnHoch4);
//        arrVerwaltung[10]   := FloatToStrF(-dVerwaltung,ffFixed,0,4);//MyFloatToStr4(-dVerwaltung);
        arrVerwaltung[10]   :=  MyFloatToStr4(-dVerwaltung);
        i:= CheckVergleich(arrSource[5]);
        IF i  <> 0 then
          SchreibeArrs(i)
        else
          //Ausgabe Original
          ConcatAusgabe(s);
        inc(rowcnt);
      end//while not eof(fIn) do

      closefile(fIn);
      flush(fOut);
      closeFile(fOut);
    setlength(OutBuf,0);
    setlength(InBuf,0);

    writeln('Bearbeitung von ',rowcnt,' Zeilen abgeschlossen');
end;
var
  i : integer;
  f : double;
Begin
  DecimalSeparator :=decSep;
  For i := 0 to 9999 do
    NachKomma[i] := Format(',%.4d',[i]);
  writeln(  NachKomma[0]);
  writeln(MyFloatToStr4(-1234.56789));
{
  For i := 0 to 1000*100 do
  begin
    f := Random*ZehnHoch4;
    IF (MyFloatToStr4(f) <> FloatToStrF(f,ffFixed,0,4)) then
      writeln(i:10,MyFloatToStr4(f),'   ',FloatToStr(round(f*ZehnHoch4)*RezZehnHoch4));
  end;
  * }

  Button1Click
END.

{$IFNDEF FPC}
   {$APPTYPE CONSOLE}
{$ENDIF}

(*
414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97
414;1231;A;2;AOGLD;I;6;2012;Eur;137,62
414;1291;A;16;EVVKWIHW;I;5;2012;Eur;0,19
414;1291;A;16;EVGLDHW;I;5;2012;Eur;0,15
414;1231;B;2;AOVKWI;I;6;2012;Eur;228,63
414;1231;B;2;AOVKWO;I;6;2012;Eur;213,66
414;1231;B;2;AOGLD;I;6;2012;Eur;141,05
414;1231;E;2;AOVKWI;I;6;2012;Eur;19,90
414;1231;E;2;AOVKWO;I;6;2012;Eur;18,60
414;1231;E;2;AOGLD;I;6;2012;Eur;13,34
414;1231;M;2;AOVKWI;I;6;2012;Eur;1117,60
414;1231;M;2;AOVKWO;I;6;2012;Eur;1044,50
*)


uses
  sysutils,classes,crt;
const
  ZehnHoch4   :double =   10000.0;
  RezZehnHoch4:double = 1/10000.0;

  maxStringCnt = 5;
  decSep = ',';
type
  tNachkomma4 = array[0..4of char;
  tArrNachkomma = array[0..9999of tNachkomma4;
  tMyString = string;
  tpChar = ^char; // oder Wchar und widestring
  tarr = Array[1..10of tMyString;
  tStringsIdx = 1..maxStringCnt;
const
  VergleichsStrings : array[tStringsIdx] of tMyString =
                       ('AOVKWO','AOGLD','EVVKWIHW','EVGLDHW','AOVKWI');
  ErsatzLogistrings : array[tStringsIdx] of tMyString =
                       ('Logi01','Logi02','Logi03','Logi04','Logi05');
  ErsatzVewastrings : array[tStringsIdx] of tMyString =
                       ('Vewa01','Vewa02','Vewa03','Vewa04','Vewa05');

  FaktorNetto    : double = 1.047;
  FaktorLogistik : double = 0.0375;
var
  Nachkomma :tArrNachkomma;
  dFaktorNetto : double;
  dFaktorLogistik: double;
  fIn, fOut : TextFile;
  sPath : string;

function ParseSem(var S: tMyString): tArr;
const
  delimiterChar = ';';
var
  pC : tpChar;
  aktPos,letztPos,k: Integer;
 begin
   pC := @S[1];
   dec(pC);// pC ist Null-basiert

   aktPos := 1;
   letztPos :=1;
   k := 1;
   repeat
     if pC[Aktpos] = delimiterChar then
     begin
       result[k]:=copy(S,letztPos,Aktpos-letztPos);
       LetztPos := Aktpos+1;
       inc(k);
     end;
     inc(Aktpos);
   until Aktpos>= length(S);
   result[k]:=copy(S,letztPos,Aktpos-letztPos+1);
end;


function RundeAuf4Stellen(zahl: double): double;
const
  ZehnHoch4   :double =   10000.0;
  RezZehnHoch4:double = 1/10000.0;
begin
  result := round(zahl*ZehnHoch4)*RezZehnHoch4;
end;

function MyFloatToStr4(d : double):string;
var
  len,i : integer;
begin
  d := round(d*ZehnHoch4)*RezZehnHoch4;
  i := trunc(d);
  result := IntToStr(i);
  len := length(result)+1;
  setlength(result,len+4);
  i := trunc(abs(d-i)*ZehnHoch4+0.5);
  Move(NachKomma[i],result[len],5);
end;

function CreateOutputS(var arrOut: tarr):tMyString;
var
  i: integer;
begin
  result :=  '';
  for i := 1 to 10 do
     result := result+ arrOut[i] + ';';
  setlength(result,Length(result)-1);
end;

Procedure ConcatAusgabe(var s: tMyString);
begin
 // writeln(fOut,s);
end;

procedure SchreibeArray(var arrOut: tarr);
var
  sOut: tMyString;
begin
   sOut:=CreateOutputS(arrOut);
   ConcatAusgabe(sOut);
end;

function CheckVergleich(var s:tMyString): integer;
var
  i : integer;
begin
  result := 0;
  For i := Low(tStringsIdx) to High(tStringsIdx) do
  begin
    IF s = VergleichsStrings[i] then
    begin
      result := i;
      Break;
    end;
  end;
end;

procedure Button1Click;
var
  dWert, dLogistik, dVerwaltung, dNetto, dAufschlag : Double;

  arrSource : tarr;
  arrNew : tarr;
  arrLogistik : tarr;
  arrVerwaltung : tarr;
  s, fName : tMyString;
  InBuf,
  OutBuf : array of Char;
  i,rowcnt: integer;

  procedure SchreibeArrs(i: integer);
  begin
    IF i in [Low(VergleichsStrings)..High(VergleichsStrings)] then
    begin
      arrVerwaltung[5] := ErsatzVewastrings[i];
      arrLogistik[5]   := ErsatzLogistrings[i];
      SchreibeArray(arrNew);
      SchreibeArray(arrVerwaltung);
      SchreibeArray(arrLogistik);
    end;
  end;

begin
//???
  dFaktorNetto := FaktorNetto;
  dFaktorLogistik := FaktorLogistik;

      fName := 'Test.csv';
      assignfile(fIn,fName);
      reset(fIn);
      fName := 'Ausgabe.csv';
      AssignFile(fOut,fName);
      setlength(InBuf,1 shl 15);
      SetTextBuf(fIn,InBuf[0],length(InBuf));


      rewrite(fOut);
      setlength(OutBuf,1 shl 15);
      SetTextBuf(fOut,OutBuf[0],length(OutBuf));
      reset(fIn);

      rowcnt := 0;
      while not eof(fIn) do
      begin
        readln(fIn,s);

        // parsen
        arrSource:= parseSem(s);
        arrNew  :=arrSource;
        arrLogistik  :=arrSource;
        arrVerwaltung  :=arrSource;

        //Rechnen
        dWert := StrToFloat(arrSource[10]);
        // Werte umrechnen
        dNetto := dWert / dFaktorNetto;
        dAufschlag := dWert - dNetto;
        dLogistik := dNetto * dFaktorLogistik;
        dVerwaltung := dAufschlag - dLogistik;
        dWert := dNetto + dLogistik;
//        arrLogistik[10]   := FloatToStr(- round(dLogistik*ZehnHoch4)*RezZehnHoch4);
//        arrLogistik[10]   := FloatToStrF(-dLogistik,ffFixed,0,4);//MyFloatToStr4(-dLogistik);
        arrLogistik[10]   :=  MyFloatToStr4(-dLogistik);
//        arrVerwaltung[10] := FloatToStr(- round(dVerwaltung*ZehnHoch4)*RezZehnHoch4);
//        arrVerwaltung[10]   := FloatToStrF(-dVerwaltung,ffFixed,0,4);//MyFloatToStr4(-dVerwaltung);
        arrVerwaltung[10]   :=  MyFloatToStr4(-dLogistik);
        i:= CheckVergleich(arrSource[5]);
        IF i  <> 0 then
          SchreibeArrs(i)
        else
          //Ausgabe Original
          ConcatAusgabe(s);
        inc(rowcnt);
      end//while not eof(fIn) do

      closefile(fIn);
      flush(fOut);
      closeFile(fOut);
    setlength(OutBuf,0);
    setlength(InBuf,0);

    writeln('Bearbeitung von ',rowcnt,' Zeilen abgeschlossen');
end;
var
  i : integer;
  f : double;
Begin
  DecimalSeparator :=decSep;
  For i := 0 to 9999 do
    NachKomma[i] := Format(',%.4d',[i]);
  writeln(  NachKomma[0]);
  writeln(MyFloatToStr4(-1234.56789));
{
  For i := 0 to 1000*100 do
  begin
    f := Random*ZehnHoch4;
    IF (MyFloatToStr4(f) <> FloatToStrF(f,ffFixed,0,4)) then
      writeln(i:10,MyFloatToStr4(f),'   ',FloatToStr(round(f*ZehnHoch4)*RezZehnHoch4));
  end;
  * }

  Button1Click
END.
{
Testdatei 6021120 Zeilen 229 Mb
Ausgabe hat 702 Mb

Bearbeitung von 6021120 Zeilen abgeschlossen

real    0m21.609s
user    0m16.566s
sys 0m0.424s

//Eingabedatei im Cache, nur zeilenweise einlesen
Bearbeitung von 0 Zeilen abgeschlossen

real    0m0.759s
user    0m0.684s
sys 0m0.073s
//300 Mb/s
}


Wären es tatsächlich nur 5 Sekunden für 700 Mb, kämen 140 Mb/s zustande, aber das Betriebssystem verwaltet die Festplatte und ist auch während der Berechnung tätig.
So sind es nur 21 Mb/s fürs schreiben.

Da aber ca 26 Sekunden nur Verarbeitungszeit sind kann man ja etwas abschätzen.
8,8 MB == 230000 Zeilen/s werden verarbeitet. So schnell sollte jede Platte lesen können.
Am besten zwei verschiedene physikalische Festplatten.Von einer nur Lesen zur anderen nur schreiben.
Wenn 700 Mb schreiben 5 sekunden zusätzlich kosten dürften 230 MB lesen auch nur knapp 2 Sekunden dauern.
Sind zusammen dann grob 40 Sekunden und auf 93 Mio Datensätze skaliert:
40 *93/6(,02) = 620 Sekunden.= 0,173 h = 188000 Zeilen/s

Gruß Horst
P.S.
Ich habe den Rechner neu gestartet:
real 0m38.108s ist doch ganz gut geschätzt :-)
P.S.S:
Concat sollte vorher mal 256 Ausgabezeilen speichern und dann in einem Rutsch ausgeben.Das brachte nur wenig.

EDIT:
Unter Lazarus funktioniert system.SetTextBuf und vielleicht statt TextFile nur Text.
Es ist sogar fast genauso schnell ( 34 Sekunden ), trotz progressbar alle 1024.
Die Datei habe ich mal angehängt.
Edit2 :
arrVerwaltung[10] := FloatToStrF(-dVerwaltung, ffFixed, 0, 4); ist wesentlich schneller
Dann 28.8 Sekunden
Für Delphi muß
ausblenden Delphi-Quelltext
1:
2:
3:
implementation

{$R *.lfm}

in
ausblenden Delphi-Quelltext
1:
2:
3:
 implementation

{$R *.dfm}
geändert werden
EDITx :
3,7 GB werden zu 11 GB in = 398 Sekunden
Zitat:

Bearbeitung von 96337920 Zeilen abgeschlossen

real 6m38.195s
user 4m23.790s
sys 0m6.274s

Mit Turbo Delphi/Win7 :
Bearbeitung von 48168960 Zeilen abgeschlossen
00:02:13.568
= 360000 Zeilen/s


5852528640 Bytes Ausgabe
Mit Turbo Delphi:
Bearbeitung von 48168960 Zeilen abgeschlossen
00:02:13.568
= 360000 Zeilen/s aka 43816847 Byte/s
*
Freepascal 2.6.4 Linux auf EXT4 Platte
Bearbeitung von 48168960 Zeilen abgeschlossen
00:02:23.261
= 336000 Zeilen/s aka 40852211 Byte/s


Windows hat wohl den bessernen NTFS-Treiber ;-)
Einloggen, um Attachments anzusehen!
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 07.07.14 19:07 
Hallo,

sodele, jetzt hochspezialisiert, denn Strings zusammenzusetzen kostet viel Zeit.
Zitat:
Bearbeitung von 48168960 Zeilen abgeschlossen
00:00:57.652

Also ca. 2 min für 96 Mio Datensätze = 800x1000 1/s

ausblenden volle Höhe 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:
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:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
program ConvCSV;

{$IFNDEF FPC}
   {$APPTYPE CONSOLE}
{$ELSE}
  {$MODE DELHI}
  {$H+}
  {$Optimization ON}
  {$Optimization Regvar}
  {$Optimization PeepHole}
  {$Optimization Asmcse}
  {$Optimization CSE}
  {$codealign loop=4,proc=32}
{$ENDIF}

(*414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97*)

uses
  sysutils,classes;
const
  AnzahlZeilen = 93*1000*1000;
  EingabeFile = 'Eingabe.csv';
  AusgabeFile = 'Ausgabe.csv';

  // Spalten der CSV Datei
  maxSpaltenCnt = 10;
  delimiterChar = ';';
  //Anzahl der verschiedenen Zuordnungen
  maxVgStringCnt = 10;

  ZehnHoch4   :double =   10000.0;
  RezZehnHoch4:double = 1/10000.0;

  decSep = ',';

type
{$IFDEF WideChar }
  tpC : pWChar;
  tMyString = widestring;
  tNachkomma4 = array[0..4of wchar;
  tArrNachkomma = array[0..9999of tNachkomma4;
{$ELSE}
  tpC = pChar;
  tMyString = string;
  tNachkomma4 = array[0..4of char;
  tArrNachkomma = array[0..9999of tNachkomma4;
{$ENDIF}

  tpChar = ^char; // oder Wchar und widestring
  tDelimPos = array[0..maxSpaltenCnt] of integer;
  tarr = Array[1..maxSpaltenCnt] of tMyString;
  TSpecArr = array[0..1]of tMyString;
  tCntVgStrings = 1..maxVgStringCnt;

  tConvert = record
                cvFakAufschlag,
                cvFakLogi : double;
                cvVgS,
                cvNewLogi,
                cvNewVewa: tMyString;
              end;
  tConvArr = array[tCntVgStrings] of tConvert;

const
  FaktorBrutto = 1.047;// Netto = 1
  FaktorAufschlag = (FaktorBrutto -1)/FaktorBrutto ;
  FaktorLogistik  = 0.0375;

  cConvArr : tConvArr = (
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'AOVKWO';
      cvNewLogi:'Logi01';cvNewVewa:'Vewa01'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'AOGLD';
      cvNewLogi:'Logi02';cvNewVewa:'Vewa02'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'EVVKWIHW';
      cvNewLogi:'Logi03';cvNewVewa:'Vewa03'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'EVGLDHW';
      cvNewLogi:'Logi04';cvNewVewa:'Vewa04'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'AOVKWI';
      cvNewLogi:'Logi05';cvNewVewa:'Vewa05'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'WEKGLDBW';
      cvNewLogi:'COWELG2013';cvNewVewa:'COWEVW2013'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'VEGLDBW';
      cvNewLogi:'COVBLG2013';cvNewVewa:'COVBVW2013'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'VKGLDBW';
      cvNewLogi:'COVKVW2013';cvNewVewa:'COVKLG2013'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'MBGLDBW';
      cvNewLogi:'COMBVW2013';cvNewVewa:'COMBLG2013'),
    ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik;
      cvVgS:'WVGLDBW';
      cvNewLogi:'COWVVW2013';cvNewVewa:'COWVLG2013')
      );
var
  Nachkomma :tArrNachkomma;
  DelimPos  :tDelimPos;

  FrontStr,
  Checkstr,
  MiddleStr,
  WertStr,
  WertLogi,
  WertVewa : tMyString;

  fIn,
  fOut : TextFile;
  InBuf,
  OutBuf : array of Char;
  hdAusgabe :boolean;

function MyFloatToStr4(d : double):string;
const
  EinHalb : double =0.5;
var
  len,i : integer;
begin
  d := round(d*ZehnHoch4)*RezZehnHoch4;
  i := trunc(d);
  result := IntToStr(i);
  len := length(result)+1;
  setlength(result,len+4);
  i := trunc(abs(d-i)*ZehnHoch4+EinHalb);
  Move(NachKomma[i],result[len],5);
end;

procedure InitNachkomma4;
var
  i : integer;
  s: string;
begin
  For i := 0 to 9999 do
  begin
    s :=  Format(',%.4d',[i]);
    move(s[1],NachKomma[i],5);
  end;
  s:= '';
end;

procedure CheckMyFloatToStr4;
var
  i : integer;
  f : double;
  s: string;
begin
  For i := 1 to 100*1000*1000 do
  begin
    f := Random*ZehnHoch4;
    s := FloatToStrF(f,ffFixed,0,4);
    IF (MyFloatToStr4(f) <> s) then
      writeln(i:10,'   ',MyFloatToStr4(f),'   ',s,'   ',f:15:8);
  end;
end;

procedure GenerateDummyFile(rowcnt: integer);

var
  s,v : string;
  i: integer;
begin
  rewrite(fIn);
  //Vorlage
  // 414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97
  i := 0;
  s := '';
  repeat
    v := Format('414;1231;A;2;%s;I;6;2012;Eur;%7.2f%s',
                [cConvArr[random(maxVgStringCnt)+1].cvVgS,random*1e4,#13#10]);
    IF length(v) +Length(s) > length(InBuf) then
      break;
    s := s+v;
    inc(i);
  until false;
  repeat
    write(Fin,s);
    dec(rowcnt,i);
  until RowCnt<0;
  reset(Fin);
end;

function OpenIOFiles(FInName,FOutName : String;MinRowCnt: integer = 1000*1000):boolean;
begin
  result := false;
  assignfile(fIn,FInName);
  {$I-}
  reset(fIn);
  IF Ioresult <> 0 then
    rewrite(fIn);
  {$I+}
  AssignFile(fOut,FOutName);
  rewrite(fOut);
  setlength(InBuf,1 shl 15);
  SetTextBuf(fIn,InBuf[0],length(InBuf));
  setlength(OutBuf,1 shl 15);
  SetTextBuf(fOut,OutBuf[0],length(OutBuf));
  IF MinRowCnt>0 then
    GenerateDummyFile(MinRowCnt);
  result := true;
end;

procedure GetDelimPos(const S: tMyString;var DelimPos:tDelimPos);
var
{$IFDEF WideChr }
  pC : pWChar;
{$ELSE}
  pC : pChar;
{$ENDIF}
  Aktpos,i : integer;
begin
  fillchar(DelimPos,SizeOf(DelimPos),#0);
  IF s = '' then
    EXIT;
  pC := @S[1];
  dec(pC); // Null basiert
  Aktpos := 1;
  DelimPos[0] := 0;// Als waere der Delimiter vorm 1. Zeichen
  i := 1;
  repeat
     if pC[Aktpos] = delimiterChar then
     begin
       DelimPos[i]:=AktPos;
       inc(i);
     end;
     inc(Aktpos);
   until Aktpos > length(S);
   DelimPos[i] := Aktpos;
end;

function GetDelimStr(var S:string;von,bis:integer):tmyString;
begin
  result := copy(S,von,Bis-von);
end;

Procedure AppendAusgabe( s: tMyString);
begin
  If hdAusgabe then
    writeln(fOut,s);
end;

procedure SchreibeArray2(var name,wert:TMyString);
begin
  AppendAusgabe(FrontStr+name+MiddleStr+wert);
end;

procedure SchreibeArrs2(i: integer);
begin
with cConvArr[i] do
  begin
    SchreibeArray2(cvNewLogi,WertLogi);
    SchreibeArray2(cvNewVewa,WertVeWa);
  end;
end;

procedure CalcConv2(Cnv:tConvert);
var
  dNetto,
  dWert,
  dAufschlag,
  dLogistik,
  dVerwaltung : Double;
{  tConvert = record
                cvFakAufschlag,
                cvFakLogi : double;
                cvVgS,
                cvNewLogi,
                cvNewVewa: tMyString;
              end;}

begin
  with Cnv do
  begin
    dWert := StrToFloat(WertStr);
    // Werte umrechnen
    dAufschlag   := dWert * cvFakAufschlag;
    dNetto       := dWert - dAufschlag;
    dLogistik    := dNetto * cvFakLogi;
    dVerwaltung  := dAufschlag - dLogistik;
    // Wo wird dieser denn noch gebraucht??
    dWert        := dNetto + dLogistik;
    //WertLogi:=FloatToStrF(-dLogistik,ffFixed,0,4)
    WertLogi     :=  MyFloatToStr4(-dLogistik);//FloatToStrF(-dLogistik,ffFixed,0,4)
    WertVeWa     :=  MyFloatToStr4(-dVerwaltung);
  end;
end;

function CheckVergleich2(var s:tMyString): integer;
var
  i : integer;
begin
  result := 0;
  // Man koennte auch eine Hashfunktion nutzen um i direkt zu finden
  For i := Low(tCntVgStrings) to High(tCntVgStrings) do
  begin
    IF s = cConvArr[i].cvVgs then
    begin
      //Rechnen
      CalcConv2(cConvArr[i]);
      result := i;
      Break;
    end;
  end;
end;

procedure GetStrings(var s: tMyString);
begin
  //mit delimiter am Ende
  FrontStr := GetDelimStr(s,DelimPos[0],DelimPos[4]);
  //ohne delimiter
  Checkstr := GetDelimStr(s,DelimPos[4]+1,DelimPos[5]);
  //mit delimitern am Anfang und Ende
  MiddleStr:=GetDelimStr(s,DelimPos[5],DelimPos[9]+1);
  //ohne delimiter
  WertStr:=GetDelimStr(s,DelimPos[9]+1,DelimPos[10]);
//  writeln(FrontStr);writeln(Checkstr); writeln(MiddleStr); writeln(Wertstr);
end;

procedure Button2Click(rowcnt:integer);
//rowcnt <= 0 -> keine neue Datei
var
  s : tMyString;
  i: integer;
  T1,T0 : TDateTime;
begin
  T0:= time;
  IF NOT(OpenIOFiles(EingabeFile,AusgabeFile,rowcnt)) then
  Begin
    writeln('Das war wohl nichts mit dem File oeffnen');
    EXIT;
  end;
  T1:= time;
  writeln('Zeit neue Datei erstellen ',FormatDateTime('NN:SS.ZZZ',T1-T0));
  rowcnt := 0;
  while not eof(fIn) do
  begin
    readln(fIn,s);
    // parsen
    GetDelimPos(s,Delimpos);
    GetStrings(s);
   //Original wird scheinbar immer ausgegeben
    AppendAusgabe(s);
    i:= CheckVergleich2(Checkstr);
    IF i  <> 0 then
      SchreibeArrs2(i);
    inc(rowcnt);
  end//while not eof(fIn) do

  closefile(fIn);
  flush(fOut);// passiert eigentlich bei Close
  closeFile(fOut);
  setlength(OutBuf,0);
  setlength(InBuf,0);

  writeln('Bearbeitung von ',rowcnt,' Zeilen abgeschlossen');
end;


var
  t1,t0: TDateTime;
Begin
  DecimalSeparator :=decSep;
  InitNachkomma4;
  // Keine Hd-Ausgabe
  hdAusgabe := true;
  T0 := time;
  Button2Click(10*1000*1000);//<= 0 ->
  T1 := time;
  Writeln(FormatDateTime('HH:NN:SS.ZZZ',T1-T0));
  readln;
END.


Gruß Horst


Zuletzt bearbeitet von Horst_H am Di 08.07.14 23:23, insgesamt 2-mal bearbeitet
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 07.07.14 20:44 
Hast du auch die Dateien, mit denen du getestet hast?
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Di 08.07.14 06:30 
Hallo,

Aus Ermangelung an Daten:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
(*
414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97
414;1231;A;2;AOGLD;I;6;2012;Eur;137,62
414;1291;A;16;EVVKWIHW;I;5;2012;Eur;0,19
414;1291;A;16;EVGLDHW;I;5;2012;Eur;0,15
414;1231;B;2;AOVKWI;I;6;2012;Eur;228,63
414;1231;B;2;AOVKWO;I;6;2012;Eur;213,66
414;1231;B;2;AOGLD;I;6;2012;Eur;141,05
414;1231;E;2;AOVKWI;I;6;2012;Eur;19,90
414;1231;E;2;AOVKWO;I;6;2012;Eur;18,60
414;1231;E;2;AOGLD;I;6;2012;Eur;13,34
414;1231;M;2;AOVKWI;I;6;2012;Eur;1117,60
414;1231;M;2;AOVKWO;I;6;2012;Eur;1044,50
auf 48168960 Zeilen hintereinander kopiert
*)

Zudem werden in Ronalds Unit.pas gänzlich andere Werte benutzt ( siehe in cConvArr bei mir )auert es 50 Sekunden.
Mal als Bild:
Erstes Einlesen-> Einlesen aus Cache-> Einlesen und Verarbeiten-> Einlesen,verarbeiten und wegschreiben
Man sieht readln mit passendem Textbuf ist recht flott.

Ausgaben

Gruß Horst

Delphi Projekt (zip, 4.21 KB)
Einloggen, um Attachments anzusehen!