Seite 1 von 2

Textdatei in Array laden

Verfasst: 25.04.2010 12:04
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:

Re: Textdatei in Array laden

Verfasst: 25.04.2010 12:28
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

Re: Textdatei in Array laden

Verfasst: 25.04.2010 12:52
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:

Re: Textdatei in Array laden

Verfasst: 25.04.2010 12:57
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.

Re: Textdatei in Array laden

Verfasst: 25.04.2010 13:34
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:

Re: Textdatei in Array laden

Verfasst: 25.04.2010 13:49
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.

Re: Textdatei in Array laden

Verfasst: 25.04.2010 14:10
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.

Re: Textdatei in Array laden

Verfasst: 25.04.2010 16:10
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

Re: Textdatei in Array laden

Verfasst: 25.04.2010 16:43
von ts-soft
vielleicht den hier nochmal testen:

Code: Alles auswählen

Steht im Eröffnungsposting 

Re: Textdatei in Array laden

Verfasst: 25.04.2010 17:17
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.