문제

My program is working almost as it should. The intended purpose is to read the file from the end and copy the contents to destination file. However what confuses me is the lseek() method more so how I should be setting the offset.

My src contents at the moment are:
Line 1
Line 2
Line 3

At the moment what I get in my destination file is:
Line 3
e 2
e 2...

From what I understand calling int loc = lseek(src, -10, SEEK_END); will move the "cursor" in source file to then end then offset it from EOF to SOF for 10 bytes and the value of loc will be the size of file after I have deducted the offset. However after 7h of C I'm almost brain dead here.

int main(int argc, char* argv[])
{
    // Open source & source file
    int src = open(argv[1], O_RDONLY, 0777);
    int dst = open(argv[2], O_CREAT|O_WRONLY, 0777);

    // Check if either reported an erro
    if(src == -1 || dst == -1)
    {
        perror("There was a problem with one of the files.");
    }

    // Set buffer & block size
    char buffer[1];
    int block;

    // Set offset from EOF
    int offset = -1;

    // Set file pointer location to the end of file
    int loc = lseek(src, offset, SEEK_END);

    // Read from source from EOF to SOF
    while( loc > 0 )
    {
        // Read bytes
        block = read(src, buffer, 1);

        // Write to output file
        write(dst, buffer, block);

        // Move the pointer again
        loc = lseek(src, loc-1, SEEK_SET);
    }

}
도움이 되었습니까?

해결책

lseek() doesn't change or return the file size. What it returns is the position where the 'cursor' is set to. So when you call

loc = lseek(src, offset, SEEK_END);

twice it will always set the cursor to the same position again. I guess you want to do something like this:

while( loc > 0 )
{
    // Read bytes
    block = read(src, buffer, 5);

    // Write to output file
    write(dst, buffer, block);

    // Move the pointer again five bytes before the last offset
    loc = lseek(src, loc+offset, SEEK_SET);
}

If the line length is variable, you could do something like the following instead:

// define an offset that exceeds the maximum line length
int offset = 256;
char buffer[256];
// determine the file size
off_t size = lseek( src, 0, SEEK_END );
off_t pos = size;
// read block of offset bytes from the end
while( pos > 0 ) {
    pos -= offset;
    if( pos < 0 ) {
        //pos must not be negative ...
        offset += pos;   // in fact decrements offset!!
        pos = 0;
    }
    lseek( src, pos, SEEK_SET );
    // add error checking here!!
    read(src, buffer, offset );
    // we expect the last byte read to be a newline but we are interested in the one BEFORE that
    char *p = memchr( buffer, '\n', offset-1 );
    p++;  // the beginning of the last line
    int len = offset - (p-buffer);  // and its length
    write( dst, p, len );
    pos -= len;            // repeat with offset bytes before the last line
}

다른 팁

From some of your comments it looks like you want to reverse the order of the lines in a text file. Unfortunately you're not going to get that with such a simple program. There are several approaches you can take, depending on how complicated you want to get, how big the files are, how much memory is on hand, how fast you want it to be, etc.

Here are some different ideas off the top of my head:

  • Read your whole source file at once into a single memory block. Scan through the memory block forwards looking for line breaks and recording the pointer and length for each line. Save these records onto a stack (you could use a dynamic array, or an STL vector in C++,) and then to write your output file, you just pop a line's record off the stack (moving backwards through the array) and write it until the stack is empty (you've reached the beginning of the array.)

  • Start at the end of your input file, but for each line, seek backwards character-by-character until you find the newline that starts the previous line. Seek forwards again past that newline and then read in the line. (You should now know its length.) Or, you could just build up the reversed characters in a buffer and then write them out backwards.

  • Pull in whole blocks (sectors perhaps) of the file at once, from end to beginning. Within each block, locate the newlines in a similar fashion to the method above except now you already have the characters in memory and so don't need to do any reversing or pulling them in redundantly. However, this solution will be much more complicated because lines can span across block boundaries.

There may be more elaborate/clever tricks, but those are the more obvious, straightforward approaches.

I think you should be using SEEK_CUR instead of SEEK_END in your final call to lseek():

// Set file pointer location to the end of file
int loc = lseek(src, offset, SEEK_END);

// Read from source from EOF to SOF
while( loc > 0 )
{
    // Read bytes
    block = read(src, buffer, 5);

    // Write to output file
    write(dst, buffer, block);

    // Move the pointer again
    lseek(src, -10, SEEK_CUR);
}

You could also do:

// Set file pointer location to the end of file
int loc = lseek(src, offset, SEEK_END);

// Read from source from EOF to SOF
while( loc > 0 )
{
    // Read bytes
    block = read(src, buffer, 5);

    // Write to output file
    write(dst, buffer, block);

    // Move the pointer again
    loc -= 5;
    lseek(src, loc, SEEK_SET);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top