How to add one line before the last line in C - c

Hi I am working in C on Unix platform. Please tell me how to append one line before the last line in C. I have used fopen in appending mode but I cant add one line before the last line.
I just want to write to the second last line in the file.

You don't need to overwrite the whole file. You just have to:
open your file in "rw" mode,
read your file to find the last line: store its position (ftell/ftello) in the file and its contents
go back to the beginning of the last line (fseek/fseeko)
write whatever you want before the last line
write the last line.
close your file.

There is no way of doing this directly in standard C, mostly because few file systems support this operation. The easiest way round this is to read the file into an in memory structure (where you probably have it anyway), insert the line in memory, then write the whole structure out again, overwriting the original file.

Append only appends to the end, not in the middle.
You need to read in the entire file, and then write it out to a new file. You might have luck starting from the back, and finding the byte offset of the second-to-last linefeed. Then you can just block write the entire "prelude", add your new line, and then emit the remaining trailer.

You can find the place where the last line ends, read the last line into memory, seek back to the place, write the new line, and then the last line.
To find the place: Seek to the end, minus a buffer size. Read buffer, look for
newline. If not found, seek backwards two buffer sizes, and try again.
You'll need to use the r+ mode for fopen.
Oh, and you'll need to be careful about text and binary modes. You need to use binary mode, since with text mode you can't compute jump positions, you can only jump to locations you've gotten from ftell. You can work around that by reading through the entire file, and calling ftell at the beginning of each line. For large files, that is going to be slow.

Use fseek to jump to end of file, read backwards until you encounter a newline. Then insert your line.
You might want to save the 'last line' you are reading by counting how many chars you are reading backwards then strncpy it to a properly allocated buffer.

Related

Most efficient way to replace a line in a text document ?

I am learning to code in Unix with C. So far I have written the code to find the index of the first byte of the line that I want to replace. The problem is that sometimes, the number of bytes replacing the line might be greater than the number of bytes already on the line. In this case, the code start overwriting the next line. I came up with two standard solutions:
a) Rather than trying to edit the file in-place, I could copy the entire file into memory, edit it by shifting all the bytes if necessary and rewriting it back to file.
b) Only copy the line I want to end-of-file to memory and edit.
Both suggestions doesn't scale well. And I don't want to impose any restrictions on the line size(like every line must be 50 bytes or something). Is there any efficient way to do the line replacement ? Any help would be appreciated.
Copy the first part of the file to a new file (no need to read it all into memory). Then, write the new version of the line. Finally, copy the final part of the file. Swap files and done.

How to move back to the starting of previous line after reading a line using fgets()?

For example if the file contains:
12345
-3445654
1245646
I want to read the first line into a string using fgets(). Then I want to read the second line in too check if there is a '-' in the first spot. If there is one, I will read the second line and strcat it to the first line.
Then I want to read the thrid line using fgets() again. This time when there is no '-' I just want to make the file go back to the beginning of the third line so that the next time I call fgets() it will read the same third line again.
Is there a way I can do this?
Use fgetc to read the first character on the next line, and if it's not a '-' use ungetc to put it back.
Generally you would just keep the part of the file just read, in the memory until you are sure you don't need it anymore.
Or you could read the entire file into a buffer and then jump around it using pointer as much as you like.
Or if you really must, you can more the current stream position with fseek, and then re-read the parts you need.
Whenever I want to
read a line
read a second line
maybe do something involving both the first line and the second line
what I usually do is declare a second line-holding variable
char prevline[whateversize];
and then, somewhere between step 1 and step 2
strcpy(prevline, line);
(Naturally you have to be sure that the line and prevline variables are consistently allocated, e.g. as arrays of the same size, so that overflow isn't a problem.)

Text files edit C

I have a program which takes data(int,floats and strings) given by the user and writes it in a text file.Now I have to update a part of that written data.
For example:
At line 4 in file I want to change the first 2 words (there's an int and a float). How can I do that?
With the information I found out, fseek() and fputs() can be used but I don't know exactly how to get to a specific line.
(Explained code will be appreciated as I'm a starter in C)
You can't "insert" characters in a file. You will have to create program, which will read whole file, then copy part before insert to a new file, your edition, rest of file.
You really need to read all the file, and ignore what is not needed.
fseek is not really useful: it positions the file at some byte offset (relative to the start or the end of the file) and don't know about line boundaries.
Actually, lines inside a file are an ill defined concept. Often a line is a sequence of bytes (different from the newline character) ended by a newline ('\n'). Some operating systems (Windows, MacOSX) read in a special manner text files (e.g. the real file contains \r\n to end each line, but the C library gives you the illusion that you have read \n).
In practice, you probably want to use line input routines notably getline (or perhaps fgets).
if you use getline you should care about free-ing the line buffer.
If your textual file has a very regular structure, you might fscanf the data (ignoring what you need to skip) without caring about line boundaries.
If you wanted to absolutely use fseek (which is a mistake), you would have to read the file twice: a first time to remember where each line starts (or ends) and a second time to fseek to the line start. Still, that does not work for updates, because you cannot insert bytes in the middle of a file.
And in practice, the most costly operation is the actual disk read. Buffering (partly done by the kernel and <stdio.h> functions, and partly by you when you deal with lines) is negligible.
Of course you cannot change in place some line in a file. If you need to do that, process the file for input, produce some output file (containing the modified input) and rename that when finished.
BTW, you might perhaps be interested in indexed files like GDBM etc... or even in databases like SqlLite, MariaDb, mongodb etc.... and you might be interested in standard textual serialization formats like JSON or YAML (both have many libraries, even for C, to deal with them).
fseek() is used for random-access files where each record of data has the same size. Typically the data is binary, not text.
To solve your particular issue, you will need to read one line at a time to find the line you want to change. A simple solution to make the change is to write these lines to a temporary file, write the changes to the same temporary file, then skip the parts from the original file that you want to change and copy the reset to the temporary file. Finally, close the original file, copy the temporary file to it, and delete the temporary file.
With that said, I suggest that you learn more about random-access files. These are very useful when storing records all of the same size. If you have control over creating the orignal file, these might be better for your current purpose.

How to remove the last 2 lines of a huge file without reading it

I have a 100GB file, and need to remove the last two lines of it.
I do not want to read from it since it will take about an hour to get to the bottom of it so sed does not seem to be an option.
My disk is also too small to be able to copy that file.
What are my options here ?
Thanks.
I'm pretty sure in .Net you can open a FileStream to a file, then move the pointer to a specific byte (which you could calculate), and modify it from there. However, I'm not sure if it has to pass the whole stream when you try to save it, so this might be more useful if you needed to copy just the last 2 lines.
In C/POSIX, you should be able to use fseek with the SEEK_END option to back up a little bit from the end of the file (say 512 bytes), then read those 512 bytes into memory.
From that, you can figure out exactly where the second last line starts and then use truncate or ftruncate to actually truncate the file at that point.
If the last two lines are more than 512 bytes (ie, the start of the second last line doesn't show up in the chunk you read), just increase the value to 1024 and try again. Keep going until you find it.

overwriting a specific line on a text file?

how do I go about overwriting a specific line on a text file in c?. I have values in multiple variables that need to be written onto the file.
This only works when the new line has the same size as the old one:
Open the file in the mode a+
fseek() to the start of the file
Before reading the next line, use ftell() to note the start of the line
Read the line
If it's the line you want, fseek() again with the result from ftell() and use fwrite() to overwrite it.
If the length of the line changes, you must copy the file.
Since files (from the point of view of C's standard library) are not line-oriented, but are just a sequence of characters (or bytes in binary mode), you can't expect to edit them at the line-level easily.
As Aaron described, you can of course replace the characters that make up the line if your replacement is the exact same character count.
You can also (perhaps) insert a shorter replacement by padding with whitespace at the end (before the line terminator). That's of course a bit crude.

Resources