Deutsch
Forum

Translate beschleunigen (Translate$ oder nproc Translate)

 
- Seite 1 -



Christof
Neuß
Hallo,

ich hexe mal wieder mit großen CSV-Dateien herum. Will diese möglichst schnell und einfach in eine SQLite-Datenbank überführen. Das funktioniert auch grundsätzlich, aber es ist mir an einigen Stellen noch zu langsam.

2 Alternativen verfolge ich gerade
a) Einlesen der CSV Satz für Satz, Anpassung an Insert-Befehl und einlesen
b) Aufbau einer passenden CSV-Datei für den Import-Befehl der SQLite-Konsole und dann über Batchdatei starten.

Variante a) ist hier (erstmal) nicht maßgeblich, da Satz-Input eh eher zu langsam ist.

Bei Variante b) bekomme ich es einfach nicht hin, die SQLite-Konsole mit einem Parameter zu versehen, der TAB als Feldtrenner setzt (\t funktioniert nicht), daher möchte ich die Datei (bis 1 GB groß) vorher vom TAB befreien und z.B. ";" als Feldtrenner setzen. Ich lese dazu über Blockread immer 1.000.000 Bytes ein, bearbeite diese und schreibe sie in eine neue Datei. Experimentiert habe ich mit Translate$ und Translate als nproc. Beide brauchen bei mir ca. 30 Sekunden, bis sie 1 MB geändert haben. Ich habe dann die nproc etwas abgeändert, da ich nur ein Zeichen tauschen will

Braucht aber fast genauso lange.

Sieht einer eine Möglichkeit, dass noch - entscheidend - zu beschleunigen?

Es ist (auch) deshalb nicht ganz einfach, die CSV-Dateien in eine SQLite-DB zu übertragen, weil ich nicht immer sicher vorher weiß, mit welchem Zeichen die Felder getrennt sind und ob es sich um eine Datei mit einfachen oder WideChar handelt. Beide kriege ich mittlerweile aber relativ schnell und sicher raus.

Hier der komplette Code des Moduls:
nProc translateONE(string src,fnd,rpl)

    {

        long p=1,cc=len(src), akt=0
        case cc<1 : return src
        case src=="" : return ""
        case fnd=="" : return src
        long fndCH=fnd[1]
        string sret=""', akt=""

        while 1

            {

                case p > cc : break

                if src[p]<>fndCH

                    {

                        akt=src[p]
                        sret=sret+chr$(akt)

                    } else

                    {

                        sret=sret+rpl

                    }

                    add p,1
                    continue

                }

                return sret

            }

            Proc CSV_in_SQLite_2'hier wird mit einem Batchfile gearbeitet

                'Dateiname, der eingelesen werden soll
                'Bezeichnung der Tabelle in SQLite
                '0=keine Meldung, 1=MessageBox am Schluss
                Parameters CSVDateiname$, EinleseTabelle$, MsgBox&
                Var TempErgebnis$="Temp_CSV_f_SQLite.CSV"
                Var IstWideChar&=0
                Var EinleseDaten$=""
                Declare EinleseDaten#
                Var EinleseBytes&=1000000'Hier kann eingestellt werden, wieviele Bytes beim Blockread jedes Mal eingelesen werden
                Var Eingelesen&=0
                Var EinleseSatz$=""
                Var EinleseFelder$=""
                Var AnzEinleseFelder&=0
                Var FeldTrenner$=""
                Var SQLiteDLL&=db("slUseDLL","SQLite3.DLL")
                Var dbODVAss&=db("slInit","ODVAss.DB")
                Var SQLBefehl$=""
                Var x&=0
                Var XX&=Fortschritt()
                Var Zaehl&=0
                Erase TempErgebnis$
                Assign #1, CSVDateiname$ : OpenRW #1
                Assign #2, TempErgebnis$ : OpenRW #2
                'Umwandlung in Datei ohne WideChar...
                Dim EinleseDaten#, EinleseBytes&
                Eingelesen&=Blockread(#1,EinleseDaten#,0,EinleseBytes&)

                if Char$(EinleseDaten#,0,1)=Chr$(255)'Datei mit WideChar

                    IstWideChar&=1

                endif

                if IstWideChar&=1

                    EinleseDaten$=EinleseDaten$+WideChar(EinleseDaten#,0,Eingelesen&)

                else

                    'normaler Zeichensatz
                    EinleseDaten$=EinleseDaten$+Char$(EinleseDaten#,0,Eingelesen&)

                endif

                EinleseFelder$=Left$(EinleseDaten$,InStr(Chr$(10),EinleseDaten$)-1)'Damit haben wir die Überschriften
                EinleseFelder$=Translate$(EinleseFelder$,Chr$(9),"¦")
                'Einlesedaten$ bleibt einfach so. Dann sind die Überschriften in der ersten Zeile
                'EinleseDaten$ =Mid$(EinleseDaten$,InStr(Chr$(10),EinleseDaten$)+1,InStr(Chr$(0),EinleseDaten$)-Len(EinleseFelder$)-2) 'Damit sind die Überschriften raus

                if Instr(Chr$(0),EinleseDaten$)<>0

                    EinleseDaten$=Left$(EinleseDaten$,Instr(Chr$(0),EinleseDaten$)-1)

                endif

                Zaehl&=Zaehl&+Len(EinleseDaten$,Chr$(10))
                Print "Anfang"
                EinleseDaten$=TranslateONE(EinleseDaten$,Chr$(9),"¦")
                Print "Ende erster Satz"
                Blockwrite #2, EinleseDaten$

                WhileLoop 10000'Der Wert wird wohl nie erreicht werden

                    SetText XX&, format$("###,###,###",&Loop)
                    Dispose EinleseDaten#
                    EinleseDaten$=""
                    Dim EinleseDaten#, EinleseBytes&
                    Eingelesen&=Blockread(#1,EinleseDaten#,0,EinleseBytes&)

                    if IstWideChar&=1

                        EinleseDaten$=WideChar(EinleseDaten#,0,Eingelesen&)

                    else

                        'normaler Zeichensatz
                        EinleseDaten$=Char$(EinleseDaten#,0,Eingelesen&)

                    endif

                    if Instr(Chr$(0),EinleseDaten$)<>0

                        EinleseDaten$=Left$(EinleseDaten$,Instr(Chr$(0),EinleseDaten$)-1)

                    endif

                    Print "Anfang "+Str$(&Loop)
                    EinleseDaten$=TranslateONE(EinleseDaten$,Chr$(9),"¦")
                    Print "Ende "+Str$(&Loop)
                    Zaehl&=Zaehl&+Len(EinleseDaten$,Chr$(10))
                    Blockwrite #2, EinleseDaten$

                    if eof(#1)

                        Break

                    endif

                EndWhile

                Close #1
                Close #2
                'Erstellen Table in dbODVAss...
                'Datenbanktabelle erstellen/bereinigen

                if db("slTableExists",dbODVAss&,EinleseTabelle$)<>0'Tabelle existiert, dann löschen

                    SQLBefehl$="Drop Table "+EinleseTabelle$
                    db("slSQLExec",dbODVAss&, SQLBefehl$,1)

                endif

                'Felder in Tabelle Tab_Felder eintragen bzw. vorher vorhandene löschen

                if db("slTableExists",dbODVAss&,"Tab_Felder")<>0'Tabelle existiert

                    SQLBefehl$="Delete from Tab_Felder where TabName='"+EinleseTabelle$+"'"
                    db("slSQLExec",dbODVAss&, SQLBefehl$,1)

                else

                    SQLBefehl$="Create Table Tab_Felder (TabName, FeldName, FeldBezei, FeldArt)"
                    db("slSQLExec",dbODVAss&, SQLBefehl$,1)

                endif

                'Feldtrenner in Originaldatei bestimmen

                if Len(EinleseFelder$,"¦")>Len(EinleseFelder$,";")

                    FeldTrenner$="¦"

                else

                    FeldTrenner$=";"

                endif

                if Len(EinleseFelder$,",")>Len(EinleseFelder$,FeldTrenner$)

                    FeldTrenner$=","

                endif

                'Felder aus der aktuellen Tabelle eintragen  ==> Feldtrenner ist dann ein Komma
                EinleseFelder$=TranslateONE(EinleseFelder$,Chr$(9),";")
                EinleseFelder$=TranslateONE(EinleseFelder$,",",".")
                EinleseFelder$=TranslateONE(EinleseFelder$,";",",")
                EinleseFelder$=Translate$(EinleseFelder$,",,,",",0,0,")
                EinleseFelder$=Translate$(EinleseFelder$,",,",",0,")
                EinleseFelder$=Translate$(EinleseFelder$,", ,",",0,")
                EinleseFelder$=TranslateONE(EinleseFelder$,":","")
                AnzEinleseFelder&=Len(EinleseFelder$,",")

                WhileLoop AnzEinleseFelder&

                    SQLBefehl$="Insert into Tab_Felder (TabName, FeldName, FeldBezei) Values ('"+EinleseTabelle$+"','F"+Format$("0000",&Loop)+"','"+Substr$(EinleseFelder$,&Loop,",")+"')"
                    db("slSQLExec",dbODVAss&, SQLBefehl$,1)

                EndWhile

                EinleseFelder$="("

                WhileLoop AnzEinleseFelder&'Danach sind in EinleseFelder$ die (neuen) F...-Bezeichnungen

                    EinleseFelder$=EinleseFelder$+"F"+Format$("0000",&Loop)+","

                EndWhile

                EinleseFelder$=left$(EinleseFelder$,Len(EinleseFelder$)-1)+")"
                SQLBefehl$="Create Table "+EinleseTabelle$+" "+EinleseFelder$
                db("slSQLExec",dbODVAss&, SQLBefehl$,1)
                db("slDone",dbODVAss&)
                FreeDll SQLiteDLL&
                'Einlesen Datensätze in dbODVAss...
                'SL-Datei erstellen
                Erase "SQLite_Import.sl"
                Assign #1, "SQLite_Import.sl" : Rewrite #1
                Print #1, "Delete from "+EinleseTabelle$+";"
                'Print #1, ".separator "+FeldTrenner$
                'Print #1, ".mode csv"
                Print #1, ".import Temp_CSV_f_SQLite.CSV "+EinleseTabelle$
                Close #1
                'Batchdatei erstellen
                Erase "SQLite_Import.bat"

                if FeldTrenner$=Chr$(9)

                    FeldTrenner$=Chr$(34)+"\\t"+Chr$(34)

                endif

                Assign #1, "SQLite_Import.bat" : Rewrite #1
                Print #1, "@echo off"
                Print #1, "SQLite3.exe -separator "+FeldTrenner$+" ODVAss.DB < SQLite_Import.SL"
                'Print #1, "Pause"
                Close #1
                ChDir $ProgDir
                SQLBefehl$="SQLite_Import.bat"
                WinExecWait(SQLBefehl$,1)
                DestroyWindow(WegwerfFenster&)
                Erase TempErgebnis$

                if MsgBox&=1

                    MessageBox("Daten wurden in die Datenbank geladen!","Meldung",0)

                endif

            Endproc


Merci und viele Grüße

Christof
 
XProfan X3
Win10 16 GB RAM
10.09.2018  
 



 
- Seite 1 -



H.Brill
3 Mio. Zeilen ist ne Menge
Ich glaube, so 260.000 ist das höchste, was die Listboxliste
aufnehmen kann. Man könnte sich aber behelfen, indem
man den index (2.Parameter) überprüft. Wenn dieser z.B.
250.000 erreicht, einfach die Liste löschen. Um das etappenweise
zu bewerkstelligen, muß man halt mit index MOD 250.000
überprüfen.
Müßte man mal probieren.
 
Benutze XPROFAN X3 + FREEPROFAN
Wir sind die XProfaner.
Sie werden von uns assimiliert.
Widerstand ist zwecklos!
Wir werden alle ihre Funktionen und Algorithmen den unseren hinzufügen.

Was die Borg können, können wir schon lange.
10.09.2018  
 




Jörg
Sellmeyer
Musst du die Datei denn unbedingt zeilenweise bearbeiten? Wenn du die Daten als CSV vorliegen hast, kannst du die doch einfach in einen String laden und den einmal mit Translate verwursten und schon sollten alle Trenner ersetzt sein.
Wenn ich es richtig in Erinnerung habe, ist die maximale Stringlänge durch den Hauptspeicher begrenzt - sollte also dann kein ernsthaftes Problem darstellen.
 
XProfan X4
Windows XP SP2 XProfan X4
... und hier mal was ganz anderes als Profan ...
10.09.2018  
 




Christof
Neuß
Tja... genau damit laboriere ich ja schon ein paar Tage rum...

- Bei ca. 260.000 Zeilen ist Schluss. Wäre ja auch zu schön gewesen um wahr zu sein. Das mit dem zweiten Parameter habe ich nicht verstanden.

- Erstens kann man 1 GB nicht mal so einfach in einen String einlesen (da kommt eher ein Speicherfehler, als man glaubt) und zweitens habe ich sowas schon versucht... Nach über 2 Stunden Laufzeit habe ich das Programm abgebrochen.

Werde ich wohl noch etwas tüfteln müssen...
 
XProfan X4
Win10 16 GB RAM
10.09.2018  
 




H.Brill
Der zweite Parameter ist halt der index.
Bei Move("ArrToList", a[]) ist es z.B. der
Index des Arrays a[index]. Bei FileToList
müßte es dann die jeweilige Zeile der Datei sein.

Das ergibt die Möglichkeit z.B. nur Teile eines
Arrays in die Listboxliste zu schieben, indem
man den index mit > oder < abfragt.

Die MoveListProc scheint da auch nicht allzu
schnell zu sein :
Declare String a[200000]
Cls
Mat a[] = "Hallo, du, da !"
Print "Fertig..."
Set("MoveListMode", 1)
Print Move("ArrToList", a[])
Print GetString$(0, 100000)
MoveListProc
Parameters String s, int index
Declare String z

If Get("MoveListMode") = 1

    z = Translate$(s, ",", "|")
    AddString z

EndIf

EndProc

WaitKey

Hier sieht man schön, wie schnell ein MAT geht
gegenüber der Proc. Schade, daß bei MAT nur feste
Ausdrücke gehen.
Übrigens : du brauchst ja bei der MoveListProc die
Listboxliste gar nicht zu benutzen. Es liegt ja in deinem
Gusto, was du mit dem String s und dem Int i anstellst.

Grob gesagt, stellt Roland den index und den dazu gehörenden
Wert (als String) der zu movenden Liste/ Array/File zur
Verfügung. Die MoveListProc ist dann, wie ein Schleifenkörper :
WhileLoop .... EndWhile
 
Benutze XPROFAN X3 + FREEPROFAN
Wir sind die XProfaner.
Sie werden von uns assimiliert.
Widerstand ist zwecklos!
Wir werden alle ihre Funktionen und Algorithmen den unseren hinzufügen.

Was die Borg können, können wir schon lange.
10.09.2018  
 




Christof
Neuß
Hi,

danke. Wieder was gelernt.

Mal sehen, ob/wie ich das gebrauchen kann.
 
XProfan X4
Win10 16 GB RAM
10.09.2018  
 




H.Brill
Vielleicht kann man ja auch mit dem Inline-ASM
etwas nachhelfen. Schau mal in Paules Forum.
Zumindest, was Schleifen betrifft. Auch das
Translate$() ließe sich damit ersetzen bzw.
wäre als ASM-Proc schneller.

Evtl. kann dir diesbezüglich jemand was basteln.
Volkmar ist da ein guter Ansprechpartner.

Schneller als ASM geht nicht mehr.
 
Benutze XPROFAN X3 + FREEPROFAN
Wir sind die XProfaner.
Sie werden von uns assimiliert.
Widerstand ist zwecklos!
Wir werden alle ihre Funktionen und Algorithmen den unseren hinzufügen.

Was die Borg können, können wir schon lange.
11.09.2018  
 




Christof
Neuß
BTW: Gibt es eine Begrenzung bei Blockread und/oder Blockwrite?
Wenn ich mehr als 1 MB einlesen will, bekomme ich eine Fehlermeldung.

Exception EAccessViolation in Modul...
Zugriffsverletzung bei Adresse...

Hab' die Grenze nicht genau ausgetestet, aber mit 1.000.000 Bytes hat's noch funktioniert, bei 1.200.000 nicht mehr.
 
XProfan X4
Win10 16 GB RAM
13.09.2018  
 




E.T.
.... grad mal probiert und via Blockread  23,5 MB in einen Bereich eingelesen, ohne Probleme....

(Ausschnitt aus einem Progg, wo ich das so ohne Probleme verwende) :
'Liste$ ist eine vorher ausgewählte Datei
Dim Bereich#, FileSize(Liste$)
BlockRead(Liste$, Bereich#, 0, FileSize(Liste$))

und dann arbeite ich einfach mit dem Bereich weiter, ist ja dann die ganze Datei drinne und ich kann damit machen, was ich will...

Wenn nat. der RAM dann ausgeht, muss man es "zerhackstückeln"
 
Grüße aus Sachsen... Mario
WinXP, Win7 (64 Bit),Win8(.1),Win10, Win 11, Profan 6 - X4, XPSE, und 'nen schwarzes, blinkendes Dingens, wo ich das alles reinschütte...
13.09.2018  
 




H.Brill
Was man auch nehmen kann, ist Frank Abbings Listview.dll.
Declare Memory bereich, Long hdll, anz
hdll = UseDLL("Listview.dll")
ImportFunc(hdll, "ExchangeSeparator", "TranslateX")
CLS
Dim bereich, FileSize("E:\Liste.txt")
Assign #1, "E:\Liste.txt"
OpenRW #1
anz = BlockRead(#1, bereich)
Close #1
Print "Fertig..."
Print "Taste zum Austausch"
WaitKey
TranslateX(bereich, FileSize("E:\Liste.txt"), Ord(","),Ord("|"),1)
Print "Fertig..."
Waitkey
Dispose bereich
FreeDLL hdll
End

Da ist keine Zeit messbar, so schnell geht das.

PS: Die ProSpeed.dll von Frank hat da noch mehr
in Sachen Bereiche zu bieten.
 
Benutze XPROFAN X3 + FREEPROFAN
Wir sind die XProfaner.
Sie werden von uns assimiliert.
Widerstand ist zwecklos!
Wir werden alle ihre Funktionen und Algorithmen den unseren hinzufügen.

Was die Borg können, können wir schon lange.
15.09.2018  
 



 
- Seite 2 -



H.Brill
Mit ASM für ein einzelnes Zeichen zu ersetzen hat mir Volkmar
eine Proc gemacht :
Declare Memory bereich
CLS
Dim bereich, 1000
String bereich, 0 = "1,", "2,", "3,", "4,", "5,", "6,", "7,", "8,", "9,", "10"
//TranslateX(Bereich#, Size&, OrgChar%, NewChar%)
ASM "TranslateX", 4
PUSH   EBX
PUSH   ECX
PUSH   EDX
PUSH   ESI
MOV    EDI, Par1// Adresse Quellstring
MOV    ECX, Par2// Länge Quellstring
MOV    EBX, Par3// Code Suchzeichen
MOV    EDX, Par4// Code Ersatzzeichen
MOV    AL, BL
MOV    AH, DL
XOR    EDX, EDX// EDX löschen
Suchen:
OR     ECX, ECX// Quelllänge 0?
JZ     IsLen// Länge 0 erreicht, String durch
DEC    ECX// Länge runter zählen
SCASB
JNZ    Suchen// Byte nicht gefunden
MOV    [EDI - 1], AH// Ersetzen
INC    EDX// Ersetzen Zählen (kann entfallen)
JMP    Suchen
IsLen:
MOV    EAX, EDX
POP    ESI
POP    EDX
POP    ECX
POP    EBX
EndASM
Print Char$(bereich, 0, 40)
Print "Stellen ersetzt : ", Str$(TranslateX(bereich, SizeOf(bereich), Ord(","), Ord("|")))
Print Char$(bereich, 0, 40)
WaitInput
Dispose bereich
End

Vielleicht kannst du es ja brauchen.
 
Benutze XPROFAN X3 + FREEPROFAN
Wir sind die XProfaner.
Sie werden von uns assimiliert.
Widerstand ist zwecklos!
Wir werden alle ihre Funktionen und Algorithmen den unseren hinzufügen.

Was die Borg können, können wir schon lange.
15.09.2018  
 




Christof
Neuß
Hallo H.

vielen Dank für Deine Bemühungen. Ja, das hilft tatsächlich schon wieder ein wenig weiter. Geht auch 'ne Ecke schneller.

Genial wäre jetzt folgende ASM-Schleife:

Aufruf mit TranslateX(Addr(Text$),Len(Text$)-1,a,b,c,d,e,f,g,h,k,l,m,n,o,p,q,r)
Wobei a-h Buchstaben(codes) sind, nach denen gesucht wird und k-r die Buchstaben(codes) sind, die dafür eingesetzt werden.
So könnte ich in einem Aufwasch gleich mehrere Ersetzungen machen (z.B. Tab gegen "|" und "/" gegen " " und ...
Dabei wird die Länge des Strings nicht verändert. Ein Zeichen wird gegen genau ein anderes Zeichen ausgetauscht.

Außerdem habe ich (leider) auch noch so etwas zu ersetzen:
"|||" gegen "|0|0|". Aber das würde die Länge des Strings verändern. Muss nicht unbedingt sein. Das geht letztlich auch über Translate$ ziemlich fix.

Gruß

Christof
 
XProfan X4
Win10 16 GB RAM
17.09.2018  
 




H.Brill
Vielleicht helfen dir da auch die regulären Ausdrücke weiter.
Mit Set("RegEx", 1) eingeschaltet, wirken die auch beim
xprofan-eigenen Translate$.
Gerade das letzte, "|||" gegen "|0|0|" auszutauschen, wäre nicht schwer.
Das Suchmuster hierfür wäre :
neuerstring$ = Translate$(alterstring$, "[|||]{3,3}", "|0|0|")

Auch für das Suchen von Gruppen [a-h] o.ä. sind ja die reg. Ausdrücke
sehr geeignet.

Lies dir mal die Hilfe dazu durch. Bei Fragen kannst du dich ja melden.
 
Benutze XPROFAN X3 + FREEPROFAN
Wir sind die XProfaner.
Sie werden von uns assimiliert.
Widerstand ist zwecklos!
Wir werden alle ihre Funktionen und Algorithmen den unseren hinzufügen.

Was die Borg können, können wir schon lange.
17.09.2018  
 




Antworten


Thementitel, max. 100 Zeichen.
 

Systemprofile:

Kein Systemprofil angelegt. [anlegen]

XProfan:

 Beitrag  Schrift  Smilies  ▼ 

Bitte anmelden um einen Beitrag zu verfassen.
 

Themenoptionen

9.792 Betrachtungen

Unbenanntvor 0 min.
Sven Bader20.03.2023
Walter05.06.2022
Rolf Koch11.11.2021
Michael Hettner02.09.2021
Mehr...

Themeninformationen



Admins  |  AGB  |  Anwendungen  |  Autoren  |  Chat  |  Datenschutz  |  Download  |  Eingangshalle  |  Hilfe  |  Händlerportal  |  Impressum  |  Mart  |  Schnittstellen  |  SDK  |  Services  |  Spiele  |  Suche  |  Support

Ein Projekt aller XProfaner, die es gibt!


Mein XProfan
Private Nachrichten
Eigenes Ablageforum
Themen-Merkliste
Eigene Beiträge
Eigene Themen
Zwischenablage
Abmelden
 Deutsch English Français Español Italia
Übersetzungen

Datenschutz


Wir verwenden Cookies nur als Session-Cookies wegen der technischen Notwendigkeit und bei uns gibt es keine Cookies von Drittanbietern.

Wenn du hier auf unsere Webseite klickst oder navigierst, stimmst du unserer Erfassung von Informationen in unseren Cookies auf XProfan.Net zu.

Weitere Informationen zu unseren Cookies und dazu, wie du die Kontrolle darüber behältst, findest du in unserer nachfolgenden Datenschutzerklärung.


einverstandenDatenschutzerklärung
Ich möchte keinen Cookie