Reverse File Read -
Novacaine - 04.06.2017
Hello,
Is there any way to read a file reverse?
Re: Reverse File Read -
NaS - 04.06.2017
Last line first, or completely reversed?
Imagine this is the content of your file:
Should it become:
or:
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.
Re: Reverse File Read -
Novacaine - 04.06.2017
Quote:
Originally Posted by NaS
Last line first, or completely reversed?
Imagine this is the content of your file:
Should it become:
or:
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.
Re: Reverse File Read -
NaS - 04.06.2017
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(fnamein, io_read);
if(!FIn) return 0;
new File:FOut = fopen(fnameout, io_write);
if(!FOut)
{
fclose(FIn);
return 0;
}
new c, i = -1, j, buf[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, i --, seek_end); // seek to end - i
c = fgetchar(FIn, 0, false); // Get character
if(c == '\r') continue;
if(c != EOF)
{
if(c != '\n') // Add to buffer
{
buf[j ++] = c;
}
else // New line - write buffer
{
if(i == -1) newline = true; // First character was a newline - keep that in mind
if(j != 0) // Buffer not empty - write
{
File_WReverse(FOut, buf, j);
fputchar(FOut, '\n', false);
j = 0;
}
}
}
else if(j != 0) // This writes the buffer because no more input is coming
{
File_WReverse(FOut, buf, j);
if(newline) fputchar(FOut, '\n', false);
j = 0;
}
}
while(c != 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 i = len - 1; i >= 0; i --) fputchar(handle, buf[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.
Re: Reverse File Read -
Novacaine - 04.06.2017
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(fnamein, io_read);
if(!FIn) return 0;
new File:FOut = fopen(fnameout, io_write);
if(!FOut)
{
fclose(FIn);
return 0;
}
new c, i = -1, j, buf[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, i --, seek_end); // seek to end - i
c = fgetchar(FIn, 0, false); // Get character
if(c == '\r') continue;
if(c != EOF)
{
if(c != '\n') // Add to buffer
{
buf[j ++] = c;
}
else // New line - write buffer
{
if(i == -1) newline = true; // First character was a newline - keep that in mind
if(j != 0) // Buffer not empty - write
{
File_WReverse(FOut, buf, j);
fputchar(FOut, '\n', false);
j = 0;
}
}
}
else if(j != 0) // This writes the buffer because no more input is coming
{
File_WReverse(FOut, buf, j);
if(newline) fputchar(FOut, '\n', false);
j = 0;
}
}
while(c != 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 i = len - 1; i >= 0; i --) fputchar(handle, buf[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.
Re: Reverse File Read -
NaS - 04.06.2017
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.
Re: Reverse File Read -
Novacaine - 05.06.2017
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 :/
Re: Reverse File Read -
NaS - 05.06.2017
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(fnamein, io_read);
if(!FIn) return 0;
new File:FOut = fopen(fnameout, io_write);
if(!FOut)
{
fclose(FIn);
return 0;
}
new c, totalchars, buf[MAX_LINE_LENGTH], flen, llen, bool:nline;
while((c = fgetchar(FIn, 0, true)) > 0) // Get file size and check if last line has a new line character
{
flen ++;
if(c == '\n') nline = false;
else nline = true;
}
if(nline) flen += 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(FIn, buf)))
{
totalchars += llen; // Keep track of how many characters were already read in total
if(nline && totalchars == flen - 2 && 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(FOut, flen - totalchars, seek_start); // Navigate to the correct offset in the new file (end - total characters read)
fwrite(FOut, buf);
}
fclose(FOut);
fclose(FIn);
return 1;
}
Should be faster and also fix the random characters
Re: Reverse File Read -
Gammix - 05.06.2017
Not sure, but this might do your work
https://sampwiki.blast.hk/wiki/Fseek
Re: Reverse File Read -
Novacaine - 05.06.2017
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(fnamein, io_read);
if(!FIn) return 0;
new File:FOut = fopen(fnameout, io_write);
if(!FOut)
{
fclose(FIn);
return 0;
}
new c, totalchars, buf[MAX_LINE_LENGTH], flen, llen, bool:nline;
while((c = fgetchar(FIn, 0, true)) > 0) // Get file size and check if last line has a new line character
{
flen ++;
if(c == '\n') nline = false;
else nline = true;
}
if(nline) flen += 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(FIn, buf)))
{
totalchars += llen; // Keep track of how many characters were already read in total
if(nline && totalchars == flen - 2 && 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(FOut, flen - totalchars, seek_start); // Navigate to the correct offset in the new file (end - total characters read)
fwrite(FOut, buf);
}
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.