Reverse File Read
#1

Hello,

Is there any way to read a file reverse?
Reply
#2

Last line first, or completely reversed?

Imagine this is the content of your file:

Код:
hello
test
Should it become:

Код:
tset
olleh
or:

Код:
test
hello
It's also important to know how long the file is. If it's not too big it's easy to use a buffer and then go through that in reverse (either line by line or character by character). Otherwise you must use fseek and fgetchar. fseek has different modes you can take advantage of.
Reply
#3

Quote:
Originally Posted by NaS
Посмотреть сообщение
Last line first, or completely reversed?

Imagine this is the content of your file:

Код:
hello
test
Should it become:

Код:
tset
olleh
or:

Код:
test
hello
It's also important to know how long the file is. If it's not too big it's easy to use a buffer and then go through that in reverse (either line by line or character by character). Otherwise you must use fseek and fgetchar. fseek has different modes you can take advantage of.
I want it to last line first. Like the second one. The file size is uncertain. It can be up to 300 or 500 lines.
Reply
#4

Okay, I wrote a function just for you to do it. There might be better ways and there is one issue - it ignores \r for now (\n will be kept). \n\r would result in two new lines.

It creates another file with the reverse line order.
You can replace the second function with what you actually want to do - whenever buf[] is written a new line is added, but note that buf is still reversed at that point. Otherwise open the newly created file (inefficient though).

It supports as many lines as you want, however there is a maximum line length (define MAX_LINE_LENGTH).

PHP код:
File_LReverse(const fnamein[], const fnameout[])
{
    
// Open the files
    
new File:FIn fopen(fnameinio_read);
    
    if(!
FIn) return 0;
    
    new 
File:FOut fopen(fnameoutio_write);
    
    if(!
FOut)
    {
        
fclose(FIn);
        return 
0;
    }
    new 
c= -1jbuf[MAX_LINE_LENGTH], bool:newline// c is the current character, i is the position (end - i), buf the temporary buffer and newline determines whether or not there was a new line at the very end (now start) of the file
    
do
    {
        
fseek(FIn--, seek_end); // seek to end - i
        
fgetchar(FIn0false); // Get character
        
if(== '\r') continue;
        if(
!= EOF)
        {
            if(
!= '\n'// Add to buffer
            
{
                
buf[++] = c;
            }
            else 
// New line - write buffer
            
{
                if(
== -1newline true// First character was a newline - keep that in mind
                
                
if(!= 0// Buffer not empty - write
                
{
                    
File_WReverse(FOutbufj);
                    
fputchar(FOut'\n'false);
                    
0;
                }
            }
        }
        else if(
!= 0// This writes the buffer because no more input is coming
        
{
            
File_WReverse(FOutbufj);
            if(
newlinefputchar(FOut'\n'false);
            
0;
        }
    }
    while(
!= EOF);
    
fclose(FOut);
    
fclose(FIn);
    
    return 
1;
}
File_WReverse(File:handle, const buf[], len// This writes a string in reverse order. This is neccessary because our buffer is already reversed! Could be avoided by writing to the buffer in reversed order from the start
{
    for(new 
len 1>= 0--) fputchar(handlebuf[i], false);

I guess it could be improved (especially the buffer part..) but it will work.
Made this in a few minutes so if there are issues tell me (tests worked well).

Example usage:

Код:
File_LReverse("input.txt", "output.txt");
I would suggest you try to directly implement it where you want to use it instead of creating a temporary file. That will speed things up especially for long files.
Reply
#5

Quote:
Originally Posted by NaS
Посмотреть сообщение
Okay, I wrote a function just for you to do it. There might be better ways and there is one issue - it ignores \r for now (\n will be kept). \n\r would result in two new lines.

It creates another file with the reverse line order.
You can replace the second function with what you actually want to do - whenever buf[] is written a new line is added, but note that buf is still reversed at that point. Otherwise open the newly created file (inefficient though).

It supports as many lines as you want, however there is a maximum line length (define MAX_LINE_LENGTH).

PHP код:
File_LReverse(const fnamein[], const fnameout[])
{
    
// Open the files
    
new File:FIn fopen(fnameinio_read);
    
    if(!
FIn) return 0;
    
    new 
File:FOut fopen(fnameoutio_write);
    
    if(!
FOut)
    {
        
fclose(FIn);
        return 
0;
    }
    new 
c= -1jbuf[MAX_LINE_LENGTH], bool:newline// c is the current character, i is the position (end - i), buf the temporary buffer and newline determines whether or not there was a new line at the very end (now start) of the file
    
do
    {
        
fseek(FIn--, seek_end); // seek to end - i
        
fgetchar(FIn0false); // Get character
        
if(== '\r') continue;
        if(
!= EOF)
        {
            if(
!= '\n'// Add to buffer
            
{
                
buf[++] = c;
            }
            else 
// New line - write buffer
            
{
                if(
== -1newline true// First character was a newline - keep that in mind
                
                
if(!= 0// Buffer not empty - write
                
{
                    
File_WReverse(FOutbufj);
                    
fputchar(FOut'\n'false);
                    
0;
                }
            }
        }
        else if(
!= 0// This writes the buffer because no more input is coming
        
{
            
File_WReverse(FOutbufj);
            if(
newlinefputchar(FOut'\n'false);
            
0;
        }
    }
    while(
!= EOF);
    
fclose(FOut);
    
fclose(FIn);
    
    return 
1;
}
File_WReverse(File:handle, const buf[], len// This writes a string in reverse order. This is neccessary because our buffer is already reversed! Could be avoided by writing to the buffer in reversed order from the start
{
    for(new 
len 1>= 0--) fputchar(handlebuf[i], false);

I guess it could be improved (especially the buffer part..) but it will work.
Made this in a few minutes so if there are issues tell me (tests worked well).

Example usage:

Код:
File_LReverse("input.txt", "output.txt");
I would suggest you try to directly implement it where you want to use it instead of creating a temporary file. That will speed things up especially for long files.
Thank you so much. It worked very well. There is one or two gliches like extra non-file character. But it can be fixed i guess Thank you so much again.
Reply
#6

Quote:
Originally Posted by Novacaine
Посмотреть сообщение
Thank you so much. It worked very well. There is one or two gliches like extra non-file character. But it can be fixed i guess Thank you so much again.
Try to set the third argument of getchar and putchar to true, which is for utf8 encoding.
Reply
#7

Quote:
Originally Posted by NaS
Посмотреть сообщение
Try to set the third argument of getchar and putchar to true, which is for utf8 encoding.
Still got the problem with UTF-8 characters :/
Reply
#8

I came up with something way simpler, I guess.
Noticed that you can write to any position in a file and it will automatically adjust its size (fills with spaces).

It now handles each line seperately, but directly writes them to the correct offset.

PHP код:
File_LReverse(const fnamein[], const fnameout[])
{
    new 
File:FIn fopen(fnameinio_read);
    if(!
FIn) return 0;
    new 
File:FOut fopen(fnameoutio_write);
    if(!
FOut)
    {
        
fclose(FIn);
        return 
0;
    }
    new 
ctotalcharsbuf[MAX_LINE_LENGTH], flenllenbool:nline;
    while((
fgetchar(FIn0true)) > 0// Get file size and check if last line has a new line character
    
{
        
flen ++;
        if(
== '\n'nline false;
        else 
nline true;
    }
    if(
nlineflen += 2// Account for the missing new line at the very end of the file. This must be known before writing, otherwise the offsets of all other lines change!
    
fseek(FIn); // Reset pointer
    
while((llen fread(FInbuf)))
    {
        
totalchars += llen// Keep track of how many characters were already read in total
        
if(nline && totalchars == flen && llen MAX_LINE_LENGTH-2// If there was no new line at the very end and this is the last line, add "\r\n"
        
{
            
strcat(buf"\r\n");
            
totalchars += 2;
        }
        
fseek(FOutflen totalcharsseek_start); // Navigate to the correct offset in the new file (end - total characters read)
        
fwrite(FOutbuf);
    }
    
fclose(FOut);
    
fclose(FIn);
    return 
1;

Should be faster and also fix the random characters
Reply
#9

Not sure, but this might do your work https://sampwiki.blast.hk/wiki/Fseek
Reply
#10

Quote:
Originally Posted by NaS
Посмотреть сообщение
I came up with something way simpler, I guess.
Noticed that you can write to any position in a file and it will automatically adjust its size (fills with spaces).

It now handles each line seperately, but directly writes them to the correct offset.

PHP код:
File_LReverse(const fnamein[], const fnameout[])
{
    new 
File:FIn fopen(fnameinio_read);
    if(!
FIn) return 0;
    new 
File:FOut fopen(fnameoutio_write);
    if(!
FOut)
    {
        
fclose(FIn);
        return 
0;
    }
    new 
ctotalcharsbuf[MAX_LINE_LENGTH], flenllenbool:nline;
    while((
fgetchar(FIn0true)) > 0// Get file size and check if last line has a new line character
    
{
        
flen ++;
        if(
== '\n'nline false;
        else 
nline true;
    }
    if(
nlineflen += 2// Account for the missing new line at the very end of the file. This must be known before writing, otherwise the offsets of all other lines change!
    
fseek(FIn); // Reset pointer
    
while((llen fread(FInbuf)))
    {
        
totalchars += llen// Keep track of how many characters were already read in total
        
if(nline && totalchars == flen && llen MAX_LINE_LENGTH-2// If there was no new line at the very end and this is the last line, add "\r\n"
        
{
            
strcat(buf"\r\n");
            
totalchars += 2;
        }
        
fseek(FOutflen totalcharsseek_start); // Navigate to the correct offset in the new file (end - total characters read)
        
fwrite(FOutbuf);
    }
    
fclose(FOut);
    
fclose(FIn);
    return 
1;

Should be faster and also fix the random characters
That's freaking awesome Thank you so much mate. I appreciate that.
SyS, Gammix, thank you for your interest either.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)