Read a text file in reverse?

Hi - VERY new to Xojo, so forgive my newbieness. I have the need to read a text file in reverse; starting at the last record in the file and progressing record by record until either the beginning or when the user (me) wants to stop. I did this on a Raspberry Pi in Python and have that code, but am wondering if this can be done in Xojo?

Secondarily, I would also like to be able to append records to the end of a text file. I “assume” this is also possible in Xojo?

Thanks for your comments…

The TextOutputStream has an Append method, which should do what you want.

Reading a file in reverse is not something that is built-in to Xojo (or Python for that matter from what I can tell). You could do it by reading the entire file in memory and processing it backwards, I suppose. Or you could do it character by character using a BinaryStream, but that seems more complicated.

The simplest way would be to use ReadAll to read the integrality of the file (very fast), then reverse the content.

To read see example at http://documentation.xojo.com/index.php/Textinputstream

To reverse, something like this should do :

[code]dim zText() as string = split(myText, “”)
dim reversed() as string

for i as integer = zText.Ubound downto 0
system.debuglog str(i)
reversed.append(zText(i))
system.DebugLog ztext(i)
next

Dim ReversedText as string = join(reversed, “”)[/code]

You can make it into a method if you need to sue it often.

Paul - it isn’t a “built-in” thing in Python - but as each record is read in a normal way, Python makes available a “pointer” to the next record. What I did was to read the entire file start to end, adding these pointers to a list. I then stepped through the list from the end towards the beginning, using the pointers to look at each successively backwards record. Much more memory-friendly than trying to store the entire file.

Michel - I will give your suggestion a try when I’ve gotten a better handle on using the application. I’ll need to study each line and try to understand what is happening.

Thanks for both responses!

To append the text, see the second example code at
http://documentation.xojo.com/index.php/Textoutputstream

I see. Seems like you could accomplish something similar using the BinaryStream.Position property.

Unless we are talking about gigabytes big documents, memory friendly is not the issue here. Anything reading a file byte per byte will be much, much slower than processing the content in memory.

Since this is a text file, what do you mean by records?
If a record refers to a fixed sized block of text, then you should be able to calculate the file pointer value by multiplying the record number by the block size and then set the file pointer value to read the block. I would recommend that unless the blocks are very large, that you should read as many blocks at one time that will conveniently fit into memory so that the file reading is more efficient.

The file can contain thousands of records. Many thousands. Each record could be anywhere from 50 - 120 or so bytes in length, with each one having an EOF at the end. This application will be used on a Raspberry Pi. When I ran a similar process written in interpretive Python, it ripped through the file in a matter of seconds. I may be wrong, but I do not believe Python hands me the data byte by byte, but rather a record at a time.

Presently I’m slowly working my way into the Xojo IDE and language, and everyone’s input has certainly helped.

Thanks folks!!

Is a record delimited by anything? A line ending (CR, LF, CRLF)? Any of the ASCII character codes for records?

An idea: preprocess the file once by reading each line and record the PositionB after each read. Once you have all the line positions, use that to read backwards through the file.

Here is some untested concept code:

dim tis as TextInputStream = TextInputStream.Open( f )
tis.Encoding = Encodings.UTF8 // or whatever

dim linePositions() as integer 
while not tis.EOF
  linePositions.Append tis.PositionB
  call tis.ReadLine
wend

//
// Now read the file
//
for index as integer = linePositions.Ubound downto 0
  tis.PositionB = linePositions( index )
  dim rec as string = tis.ReadLine
  // do something with the rec
next

Another question is whether you have control over the file format, or whether you’ve inherited it from somewhere and are stuck with it.
If you can customize the format slightly, then you can include a linked list of pointers that allows you to quickly step through the file, both forwards and backwards record by record.

Jim, your title is somewhat misleading. A text file is composed of characters essentially, and by “Reverse”, one can infer “ABC” read as “CBA”

If you have paragraphs (records separated by EndOfLine), your title should read “read paragraphs in Text file in reverse order”.

The code I posted should then be slightly revised :

MyText is the result of ReadAll using the example of TextIputStream.

[code]
MyText = ReplaceLineEndings(MyText,EndOfLine) // To make sure EndOfLine conforms to platform
dim zText() as string = split(myText, endOfLine)
dim reversed() as string

for i as integer = zText.Ubound downto 0
reversed.append(zText(i))
next

Dim ReversedText as string = join(reversed, endOfLine)[/code]

[quote=323357:@Robert Weaver]Another question is whether you have control over the file format, or whether you’ve inherited it from somewhere and are stuck with it.
If you can customize the format slightly, then you can include a linked list of pointers that allows you to quickly step through the file, both forwards and backwards record by record.[/quote]

That sounds like what you really want is a database. If you’re file isn’t a true database already, you can import it in a SQLite database and randomly access the records by whatever criteria you’d like.

Guys - I think, based on the input given so far, that I can “git it done”. The file in question is inherited - can’t change the format. But, the suggestion by Kem to record the PositionB after each read and then use them to read in reverse is exactly what I did in Python on my Raspberry Pi. I’ll give that a shot, when I return home from the Philippines later next month.

btw - yes, each record/paragraph is delimited by en EOF.

Thanks for everyone’s input - much appreciated.

And, I’m sure I’ll be back here with other questions in due time! :slight_smile:

Jim