Textdatei in Array laden

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Textdatei in Array laden

Beitrag von rolaf »

Hier drei Varianten um eine Textdatei in ein Array zu laden:

Variante 1: (zweitschnellster mit ultrakurzem Code)

Code: Alles auswählen

;Text2Array (PB 4.41)
;von DrFalo, ts-soft, ...

EnableExplicit

Procedure Text2Array(Datei.s, Array TextZeile.s(1))

  Protected Text.s, TextZeilenAnzahl, A
  Protected File = ReadFile(#PB_Any, Datei)
  Protected *StringID

  If File 
    *StringID = AllocateMemory(Lof(File))
    If *StringID
      ReadData(File, *StringID, Lof(File))
    EndIf
    CloseFile(File)
  EndIf

  Text = PeekS(*StringID)
  FreeMemory(*StringID)

  TextZeilenAnzahl = CountString(Text, #LF$)
  
  If TextZeilenAnzahl
    ReDim TextZeile(TextZeilenAnzahl)
    File = ReadFile(#PB_Any, Datei)
    If File 
      For A = 1 To TextZeilenAnzahl
        TextZeile(A) = ReadString(File)
      Next A
      CloseFile(File)
    EndIf
  EndIf

EndProcedure

Define.s Datei
Define A
Dim TextZeile.s(1)

Datei.s = OpenFileRequester("Datei auswählen...", "", "Text (.txt)|*.txt", 0)
If Datei

  Text2Array(Datei, TextZeile.s())

  Debug "Insgesamt " + Str(ArraySize(TextZeile())) + " Zeilen"
  Debug ""
  For A = 1 To ArraySize(TextZeile())
    If A = 10
      Debug "u.s.w."
      Break ; ab Zeile 10 abbrechen
    EndIf
    Debug "Zeile " + Str(A) + ":  " + TextZeile(A)
  Next A

EndIf
Variante 2: (drittschnellster mit mittellangem Code)

Code: Alles auswählen

;Text2Array (PB 4.41)
;von DrFalo, ts-soft, ...

EnableExplicit

Procedure Text2Array(Datei.s, Array TextZeile.s(1))

  Protected Text.s, TextZeilenAnzahl, A, tmp.s
  Protected File = ReadFile(#PB_Any, Datei)
  Protected *StringID, *mem_u.Character, *mem_a.Byte
  Protected Format, counter = 1

  If File
    Format = ReadStringFormat(File)
    If Format = 4 Or Format = 5 Or Format = 6
      Debug "Stringformat not supported"
      CloseFile(File)
      ProcedureReturn #False
    EndIf
    *StringID = AllocateMemory(Lof(File))
    If *StringID
      ReadData(File, *StringID, Lof(File))
    EndIf
    CloseFile(File)
  EndIf
 
  TextZeilenAnzahl = CountString(PeekS(*StringID, -1, Format), #LF$)
  ReDim TextZeile(TextZeilenAnzahl)
  If Format = #PB_Unicode
    *mem_u = *StringID
   
    While *mem_u\c <> 0
      Select *mem_u\c
        Case 13
        Case 10
          TextZeile(counter) = tmp
          tmp = ""
          counter + 1
        Default
          tmp + PeekS(*mem_u\c, 2, Format)
      EndSelect
      *mem_u + 2
    Wend
  Else
    *mem_a = *StringID
    While *mem_a\b <> 0
      Select *mem_a\b
        Case 13
        Case 10
          TextZeile(counter) = tmp
          tmp = ""
          counter + 1
        Default
          tmp + PeekS(*mem_a, 1, Format)
      EndSelect
      *mem_a + 1
    Wend
  EndIf
  FreeMemory(*StringID)
 
  ProcedureReturn TextZeilenAnzahl

EndProcedure

Define.s Datei
Define TextZeilenAnzahl, A
Dim TextZeile.s(1)

Datei = OpenFileRequester("Datei auswählen...",  "", "Text (.txt)|*.txt",  0)
If Datei

  TextZeilenAnzahl = Text2Array(Datei, TextZeile())

  Debug "Insgesamt " + Str(TextZeilenAnzahl) + " Zeilen"
  Debug ""
  For A = 1 To TextZeilenAnzahl
    If A = 10
      Debug "u.s.w."
      Break ; ab Zeile 10 abbrechen
    EndIf
    Debug "Zeile " + Str(A) + ":  " + TextZeile(A)
  Next A

EndIf 
Variante 3: (der schnellste mit dem längsten Code)

Code: Alles auswählen

;Text2Array (PB 4.50 beta 2)
;von DrFalo, ts-soft, NicTheQuick, ...

EnableExplicit

Procedure Text2Array(Datei.s, Array TextZeile.s(1))
   Protected cLines.i, fileSize.i, stringFormat.i
   Protected hFile = ReadFile(#PB_Any, Datei)
   Protected *memBegin, *mem, i.i, *memEnd, *lineBegin, ca.a, cc.c
   Protected NewList lineBegins.i()
   
   If hFile
      stringFormat = ReadStringFormat(hFile)
      
      fileSize = Lof(hFile)
      If fileSize = 0 : ProcedureReturn : EndIf
      
      *memBegin = AllocateMemory(fileSize)
      If *memBegin
         ReadData(hFile, *memBegin, fileSize)
      EndIf
      CloseFile(hFile)
   EndIf
   
   If Not *memBegin : ProcedureReturn : EndIf
   *mem = *memBegin
   *memEnd = *mem + fileSize
   
   Select stringFormat
      Case #PB_Ascii
         Debug "ASCII"
         *lineBegin = *mem
         While *mem < *memEnd
            ca = PeekA(*mem)
            Select ca
               Case #CR
                  PokeA(*mem, 0)
                  If AddElement(lineBegins())
                     lineBegins() = *lineBegin
                  EndIf
                  If PeekA(*mem + 1) = #LF : *mem + 1 : EndIf
                  *lineBegin = *mem + 1
               
               Case #LF
                  PokeA(*mem, 0)
                  If AddElement(lineBegins())
                     lineBegins() = *lineBegin
                  EndIf
                  *lineBegin = *mem + 1
            EndSelect
            *mem + 1
         Wend
         If *lineBegin < *memEnd
            If AddElement(lineBegins())
               lineBegins() = *lineBegin
            EndIf
         EndIf
      
      Case #PB_UTF8
         Debug "UTF8"
         ;TODO
         
      Case #PB_Unicode
         Debug "UNICODE"
         *lineBegin = *mem
         While *mem < *memEnd
            cc = PeekC(*mem)
            Select cc
               Case #CR
                  PokeC(*mem, 0)
                  If AddElement(lineBegins())
                     lineBegins() = *lineBegin
                  EndIf
                  If PeekC(*mem + 2) = #LF : *mem + 1 : EndIf
                  *lineBegin = *mem + 2
               
               Case #LF
                  PokeC(*mem, 0)
                  If AddElement(lineBegins())
                     lineBegins() = *lineBegin
                  EndIf
                  *lineBegin = *mem + 2
            EndSelect
            *mem + 2
         Wend
         If *lineBegin < *memEnd
            If AddElement(lineBegins())
               lineBegins() = *lineBegin
            EndIf
         EndIf
         
      Default
         ProcedureReturn
   EndSelect
   
   cLines = ListSize(lineBegins())
            
   ReDim TextZeile(cLines - 1)
   
   i = 0
   ForEach lineBegins()
      TextZeile(i) = PeekS(lineBegins(), -1, stringFormat)
      i + 1
   Next
   
   FreeMemory(*memBegin)
EndProcedure

Define.s Datei
Define A, time.i
Dim TextZeile.s(0)

Datei.s = OpenFileRequester("Datei auswählen...", "", "Text (.txt)|*.txt", 0)
If Datei
   
   time.i = ElapsedMilliseconds()
   Text2Array(Datei, TextZeile.s())
   time = ElapsedMilliseconds() - time
   
   MessageRequester("", "Zeit: " + Str(time) + " ms") ;28 ms
   
   Debug "Insgesamt " + Str(ArraySize(TextZeile()) + 1) + " Zeilen"
   Debug ""
   For A = 0 To ArraySize(TextZeile())
      If A = 200
         Debug "u.s.w."
         Break ; ab Zeile 10 abbrechen
      EndIf
      Debug "Zeile " + Str(A) + ":  " + TextZeile(A)
   Next A
   
EndIf
Edit by NicTheQuick: Hab mal meinen Namen korrigiert :D
Ja Sorry. :wink:
Zuletzt geändert von rolaf am 26.04.2010 07:00, insgesamt 5-mal geändert.
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Textdatei in Array laden

Beitrag von ts-soft »

Weil ich es gerade sehe :wink:
Das doppelte Laden kann man sich doch sparen:

Code: Alles auswählen

Steht im Eröffnungsposting
wobei hier ist sind noch Optimierungen möglich.

Gruß
Thomas
Zuletzt geändert von ts-soft am 25.04.2010 14:23, insgesamt 1-mal geändert.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Re: Textdatei in Array laden

Beitrag von rolaf »

ts-soft hat geschrieben:Weil ich es gerade sehe :wink:
Ja Adlerauge. :mrgreen:
Das hätte mir aber auch selbst einfallen können, den geladenen Text im Speicher auch gleich zu nutzen, Mist. :lol:
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Textdatei in Array laden

Beitrag von ts-soft »

DrFalo hat geschrieben:Das hätte mir aber auch selbst einfallen können, den geladenen Text im Speicher auch gleich zu nutzen, Mist. :lol:
Zumal ReadString ziemlich langsam ist! So biste mind. 5x schneller ohne Nebenwirkungen.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Re: Textdatei in Array laden

Beitrag von rolaf »

So noch ein wenig Kosmetik (jedenfalls für meinen Gebrauch :mrgreen: ), dazu starte ich Arrays gerne erst ab 1 (macht vieles einfacher) und das CR-Zeichen noch aus den Strings entfernt.

Code: Alles auswählen

Steht im Eröffnungsposting
Edit:
Hoppala, stelle gerade fest das entgegen meiner ersten Version diese erheblich langsammer ist. Liegt wohl an "StringField" und "RemoveString", da ist "ReadString" wohl doch erheblich schneller... :wink:
Zuletzt geändert von rolaf am 25.04.2010 14:30, insgesamt 1-mal geändert.
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Textdatei in Array laden

Beitrag von ts-soft »

Hab die Kostmetik auch noch mal geschminkt :mrgreen:
Bisher haben wird das Stringformat des Dokuments ausser acht gelassen und das
darf man heutzutage nicht mehr machen, da es bald mehr Textdateien in UTF-8
als in ASCII gibt. Habs entsprechend angepaßt.

Code: Alles auswählen

Steht im Eröffnungsposting
@DrFalo
wäre schön, wenn Du den Source ins erste Postings plazierst, dann können wir die
Zwischenergebnisse wieder entfernen.

Gruß
Thomas

// edit
Speedproblem mit RemoveString behoben.
Zuletzt geändert von ts-soft am 25.04.2010 14:22, insgesamt 1-mal geändert.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Re: Textdatei in Array laden

Beitrag von rolaf »

> Speedproblem mit RemoveString behoben.

Versuchs mal mit ner 5000 Zeilen Textdatei, da sieht man das meine erste Variante weit weit schneller ist als deine letzte. Sehr merkwürdig das... :shock:

Edit:
Kann es sein das das Zerhacken eines so langen Textes einfach mehr Zeit
braucht als das erneute zeilenweise Laden? Ich vermute fast - Ja.
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Textdatei in Array laden

Beitrag von hjbremer »

Genau für diesen Zweck habe ich vor kurzem nach schnellen Codes für das Suchen von Zeichen im Memory gefragt. Und es kamen viele schöne Beispiele http://www.purebasic.fr/german/viewtopi ... =3&t=22116

Denn bei größeren Texten ist folgender Code aus dem Beispiel recht langsam
Text = PeekS(*StringID)
FreeMemory(*StringID)

TextZeilenAnzahl = CountString(Text, #LF$)
....
For A = 1 To TextZeilenAnzahl
TextZeile(A) = ReadString(File)
Next A
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Textdatei in Array laden

Beitrag von ts-soft »

vielleicht den hier nochmal testen:

Code: Alles auswählen

Steht im Eröffnungsposting 
Zuletzt geändert von ts-soft am 25.04.2010 17:26, insgesamt 1-mal geändert.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Re: Textdatei in Array laden

Beitrag von rolaf »

Hallo ts-soft,

deine neue Variante ist zwar schneller, aber Variante 1ist bei mir immer noch 15 mal schneller! Probiers mal selbst:

Code: Alles auswählen

;Text2Array (PB 4.41)
;von DrFalo, ts-soft, ...

EnableExplicit

Procedure Text2ArrayV1(Datei.s, Array TextZeile.s(1))


  Protected Text.s, TextZeilenAnzahl, A
  Protected File = ReadFile(#PB_Any, Datei)
  Protected *StringID

  If File 
    *StringID = AllocateMemory(Lof(File))
    If *StringID
      ReadData(File, *StringID, Lof(File))
    EndIf
    CloseFile(File)
  EndIf

  Text = PeekS(*StringID)
  FreeMemory(*StringID)

  TextZeilenAnzahl = CountString(Text, #LF$)
  
  If TextZeilenAnzahl
    ReDim TextZeile(TextZeilenAnzahl)
    File = ReadFile(#PB_Any, Datei)
    If File 
      For A = 1 To TextZeilenAnzahl
        TextZeile(A) = ReadString(File)
      Next A
      CloseFile(File)
    EndIf
  EndIf

EndProcedure

Procedure Text2ArrayV2(Datei.s, Array TextZeile.s(1))

  Protected Text.s, TextZeilenAnzahl, A, tmp.s
  Protected File = ReadFile(#PB_Any, Datei)
  Protected *StringID, *mem_u.Character, *mem_a.Byte
  Protected Format, counter = 1

  If File
    Format = ReadStringFormat(File)
    If Format = 4 Or Format = 5 Or Format = 6
      Debug "Stringformat not supported"
      CloseFile(File)
      ProcedureReturn #False
    EndIf
    *StringID = AllocateMemory(Lof(File))
    If *StringID
      ReadData(File, *StringID, Lof(File))
    EndIf
    CloseFile(File)
  EndIf
 
  TextZeilenAnzahl = CountString(PeekS(*StringID, -1, Format), #LF$)
  ReDim TextZeile(TextZeilenAnzahl)
  If Format = #PB_Unicode
    *mem_u = *StringID
   
    While *mem_u\c <> 0
      Select *mem_u\c
        Case 13
        Case 10
          TextZeile(counter) = tmp
          tmp = ""
          counter + 1
        Default
          tmp + PeekS(*mem_u\c, 2, Format)
      EndSelect
      *mem_u + 2
    Wend
  Else
    *mem_a = *StringID
    While *mem_a\b <> 0
      Select *mem_a\b
        Case 13
        Case 10
          TextZeile(counter) = tmp
          tmp = ""
          counter + 1
        Default
          tmp + PeekS(*mem_a, 1, Format)
      EndSelect
      *mem_a + 1
    Wend
  EndIf
  FreeMemory(*StringID)

EndProcedure

Define.s Datei
Define A
Dim TextZeile.s(1)
Define TimeStart, TimeEnd

Datei = OpenFileRequester("Datei auswählen...", "", "Text (.txt)|*.txt", 0)

If Datei

  TimeStart = ElapsedMilliseconds()
  For A = 1 To 100
    Text2ArrayV1(Datei, TextZeile())
  Next A
  TimeEnd  = (ElapsedMilliseconds()-TimeStart)/100

  Debug "Insgesamt " + Str(ArraySize(TextZeile())) + " Zeilen"
  Debug ""
  For A = 1 To ArraySize(TextZeile())
    If A = 10
      Debug "u.s.w."
      Break ; ab Zeile 10 abbrechen
    EndIf
    Debug "Zeile " + Str(A) + ":  " + TextZeile(A)
  Next A

EndIf
Debug "Variante 1 braucht " + Str(TimeEnd) + " ms"

ReDim TextZeile.s(1)

If Datei

  TimeStart = ElapsedMilliseconds()
  For A = 1 To 100
    Text2ArrayV2(Datei, TextZeile())
  Next A
  TimeEnd  = (ElapsedMilliseconds()-TimeStart)/100

  Debug "Insgesamt " + Str(ArraySize(TextZeile())) + " Zeilen"
  Debug ""
  For A = 1 To ArraySize(TextZeile())
    If A = 10
      Debug "u.s.w."
      Break ; ab Zeile 10 abbrechen
    EndIf
    Debug "Zeile " + Str(A) + ":  " + TextZeile(A)
  Next A

EndIf
Debug "Variante 2 braucht " + Str(TimeEnd) + " ms"
Tja hjbremer, ganz so einfach ist es nicht wie du siehst. Der von dir als langsam titulierte Ausschnitt ist leider um längen der Schnellste. :mrgreen:

Ich denke wir sollten es dabei belassen, weil bo³ sonst wieder mit dem Manta und Fuchsschwanz kommt (siehe von hjbremer verlinkten Thread). :lol: Ich kopier deine neue Variante aber als Variante 2 wieder in den Startpost.
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Antworten