C - Deleting / Modifiying A line From A File - c

I have a datas.txt file :
format : name surname debt payment
bir bir 100 2
iki iki 200 2
eray alakese 100 5
john doe 2000 10
I'm learning C and i know just simple file functions (fscanf, fprinf, fopen etc.)
I'll
ask user name and surname with scanfand then assign them to name and surname variables.
It will search file for name and surname and then assign debt and payment to debt, payment variables ( fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);)
Delete or modify this line
This is my source code .
scanf("%s", &name);
scanf("%s", &surname);
file = fopen("datas.txt", "r");
/* this fscanf() is working as expected. There is no problem. */
fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);
/* modify and delete actions here */
fclose(file);
Examples :
I want to delete record of "John Doe" .
I want to decrease "John Doe"'s debt to 100$

You cannot delete/modify[*] individual lines of a text file; the only solution is to 1) create a new temporary file, 2) copy the contents up to, but not including, the line that you want modified/deleted, 3) output the modified line, 4) copy the rest of the original file, 5) replace the old file with the temporary file.
[*] Modification is possible ONLY if the modified line has the same length as the original line.
EDIT: PS: Using fgets, followed by sscanf (or some other way of tokenizing the line) will save you much grief.

This is a little hard to do, because C's model of files, inherited from Unix (they were largely codeveloped), does not actually define a file as a list of lines. Instead, it defines a line as a string of bytes terminated with a newline, and a file (roughly) as a stored string of bytes of potentially limited length, where you can possibly skip to different parts. That's considerably vague, but bear with me.
Whe problem becomes clearer when we try to translate our ideas - "modify this line", "delete that line" - into file operations. We can read a line by just stopping at a newline, but there's simply no command to cut it into sections; only to set the end (ftruncate()). So to change the size of the line, we need to copy all the data that follows it. It can be done, but it's very often easier to just create the file anew. Compare the subtleties of implementing memmove().
The traditional method to do this comes in two variants, depending on what side effects you can tolerate.
One is to write your updated version in another file, and then rename() it into place. This has the advantage that the new file will be complete by the time you put it in place, but the downsides that it may not match the old file precisely as far as permissions etc go, and it won't be visible to other programs that already had the old one open. If two programs modify the file like this, it's a race condition as one of the changes gets overwritten by the other.
The other is to load the data completely and write the modified version down in place. This means the file itself remains in place, permissions and all, but there will be a duration while you're saving that it is a mix of old and new contents. Text editors tend to do this, often whilst saving the old contents as a separate file in case something goes wrong.
There are tools to manage the side effects too, such as versioned filesystems, file locking, and even libraries prepared for parallel changes (metakit comes to mind). Most of the time we'll be using tools that are already around, like sed -i.

In order to delete or alter a line, you have to "shift" everything after it. For example, consider these two files:
bir bir 100 2 bytes 0-14
iki iki 200 2 bytes 15-29
eray alakese 100 5 bytes 30-49
john doe 2000 10 bytes 50-67
and
bir bir 100 2 bytes 0-14
iki iki 200 2 bytes 15-29
john doe 2000 10 bytes 30-57 <-- byte offsets have changed
This is certainly possible to do, but it's pretty complicated to support in general (you'd have to do a lot of seeks and tells). The more usual approach is to effectively copy the file: you read in from your input-file and print everything out to an output-file, making the modifications you need. (For example, to "delete" a line, you simply don't print that line.) Then, at the end, after closing both files, you "rename" the output-file to overwrite the input-file. This is the approach that command-line utilities such as sed and perl use when instructed to modify a file "in-place".

The usual thing to do is to read all of the file and write all of it back to a temporary file, then delete the original and rename the temporary.
/* pseudo-code!! */
fopen();
while (fscanf(source, ...)) {
/* massage data */
fprintf(temporary, ...);
}
fclose();
remove(source);
rename(temporary, source);

The way I usually handle something like this is to write a function that can "read in" your data and store it to some structure. Then a function to write data from the structure to a file.
This way you can just manipulate the data in an array. This also makes your program more extensible to doing things like sorting, or extra math that you couldnt have done by just writing over the top of the file.
e.g. try writing a function that can read in to a struct like:
struct Client
{
char name[255];
double owes;
double paid;
}
What you then do make an array of these structures and manipulate those.
You'll learn a lot about structs, dynamic memory allocation, and you'll no doubt run in to some interesting issues that will help you learn.
My advice is to also skip C and go for C++... learning this stuff using iostreams instead of the *printf/*scanf functions and vectors is probably going to be better for you in the long run

Related

How to write at the middle of a file in c

Is it possible to write at the middle of a file for example I want to insert some string at the 5th position in the 2nd line of a file in c ?
I'm not very familiar with some of C functions that are related to handling files , if someone could help me I would appreciate it
I tried using fputs but I couldn't insert characters at the desired location
open a new output file
read the input file line by line (fgets) writing each line out to a new file as you read.
When you hit the place you want to insert write the new line(s)
The carry on copy the old lines to the new file
close input and output
rename output file to input
Continuing from my comments above. Here's what I'd do:
Create two large, static char[] buffers of the same size--each large enough to store the largest file you could possibly ever need to read in (ex: 10 MiB). Ex:
#define MAX_FILE_SIZE_10_MIB (10*1024*1024)
static char buffer_file_in[MAX_FILE_SIZE_10_MIB];
static char buffer_file_out[MAX_FILE_SIZE_10_MIB];
Use fopen(filename, "r+") to open the file as read/update. See: https://cplusplus.com/reference/cstdio/fopen/. Read the chars one-by-one using fgetc() (see my file_load() function for how to use fgetc()) into the first large char buffer you created, buffer_file_in. Continue until you've read the whole file into that buffer.
Find the location of the place you'd like to do the insertion. Note: you could do this live as you read the file into buffer_file_in the first time by counting newline chars ('\n') to see what line you are on. Copy chars from buffer_file_in to buffer_file_out up to that point. Now, write your new contents into buffer_file_out at that point. Then, finish copying the rest of buffer_file_in into buffer_file_out after your inserted chars.
Seek to the beginning of the file with fseek(file_pointer, 0, SEEK_SET);
Write the buffer_file_out buffer contents into the file with fwrite().
Close the file with fclose().
There are some optimizations you could do here, such as storing the index where you want to begin your insertion, and not copying the chars up to that point into buffer_file_in, but rather, simply copying the remaining of the file after that into buffer_file_in, and then seeking to that point later and writing only your new contents plus the rest of the file. This avoids unnecessarily rewriting the very beginning of the fie prior to the insertion point is all.
(Probably preferred) you could also just copy the file and the changes you insert straight into buffer_file_out in one shot, then write that back to the file starting at the beginning of the file. This would be very similar to #pm100's approach, except using 1 file + 1 buffer rather than 2 files.
Look for other optimizations and reductions of redundancy as applicable.
My approach above uses 1 file and 1 or 2 buffers in RAM, depending on implementation. #pm100's approach uses 2 files and 0 buffers in RAM (very similar to what my 1 file and 1 buffer approach would look like), depending on implementation. Both approaches are valid.

replace a substring in a string in C, windows

I want to do the following:
open and read and ASCII file
locate a substring (geographical coordinates)
create its replacement (apply corrections to the original coordinates)
overwrite the original substring (write in the original file the corrected coordinates).
The format of the ASCII file is:
$GPGGA,091306.00,4548.17420,N,00905.47990,E,1,09,0.87,233.5,M,47.2,M,,*53
I will paste here only the part of the code that is responsible for this operation:
opnmea = fopen (argv[1], "r+");
if (fgets(row_nmea, ROW, opnmea)==NULL){
if (strstr(row_nmea,"$GPGGA")!=NULL) {
sscanf(row_nmea+17, "%10c", old_phi);
sscanf(row_nmea+30, "%11c", old_lam);
sscanf(row_nmea+54, "%5c", old_h);
fputs();
}
}
What I do till now is to extract in a variable the old coordinates and I was thinking to use fputs() for overwriting the old with new values. But I could not do it. The other part of the code that is not here is computing the correct coordinates. My idea is to correct the rows one by one, as the fgets() function reads each line.
I would appreciate very much any suggestion that can show me how to use fputs() or another function to complete my work. I am looking for something simple as I am beginner with C.
Thank you in advance.
Patching a text file in place is not a good solution for this problem, for multiple reasons:
the modified version might have a different length, hence patching cannot be done in place.
the read-write operation of standard streams is not so easy to handle correctly and defeats the buffering mechanism.
if you encounter an error during the patching phase, a partially modified file can be considered corrupted as one cannot tell which coordinates have been modified and which have not.
other programs might be reading from the same file as you are writing it. They will read invalid or inconsistent data.
I strongly recommend to write a program that reads the original file and writes a modified version to a different output file.
For this you need to:
open the original file for reading opnmea = fopen(argv[1], "r");
open the output file for writing: outfile = fopen(temporary_file_name, "w");
copy the lines that do not require modification: just call fputs(row_nmea, outfile).
parse relevant data in lines that require modification with whatever method you are comfortable with: sscanf, strtok, ...
compute the modified fields and write the modified line to outfile with fprintf.
Once the file has been completely and correctly handled, you can replace the original file with rename. The rename operation is usually atomic at the file-system level, so other programs will either finish reading from the previous version or open the new version.
Of course, if the file has only one line, you could simply rewind the stream and write back the line with fprintf, but this is a special case and it will fail if the new version is shorter than the original. Truncating the extra data is not easy. An alternative is to reopen the file in write mode ("w") before writing the modified line.
I would recommend strtok(), followed by your revision, followed by strcat().
strtok() will let you separate the line using the comma as a delimiter, so you will get the field you want reliably. You can break up the line into separate strings, revise the coordinates you wish, and reassemble the line, including the commas, with strcat().
These pages include nice usage examples, too:
http://www.cplusplus.com/reference/cstring/strtok/
http://www.cplusplus.com/reference/cstring/strcat/?kw=strcat

C - dynamically modifying a file - is it possible?

I'm writing a small program in C and I want to have the option of saving data to file and then reading it from that file. The data is BIG, so I want to somehow dynamically write to a file without having to create a new file and copy modified old file into it.
Here's exactly what I want to do:
In the first line, I want to have "description" of the data in the form "%s %s %s ... %s \n" where %s is a string and the n'th string describes data in n+1'th line. I want to read the 1'st line of the file, scan for corresponding "description" string, and if it is not present, append it to the first line, and the data corresponding to it after the last line of the file.
The question is - is it possible to "jump" into lines in the file without scanning all the previous lines, and can I somehow read the first line of the file and append something to it after reading? Or maybe it is not the way to go in this situation and C offers some kind of different solution?
What you want can be done using stdio and fseek(). As long as you know at what byte offset you want to go, you can overwrite and/or append anywhere in the file without reading the data before, or the data you're overwriting. What you can not easily do is insert data, i.e., open the file, split it in half and put data in between.
Not too sure if that is what you mean though...

How to write in file at specific location? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 8 years ago.
Improve this question
I have a homework assignment and we must use .txt files.
Here's my file example:
1 John 1234 558
2 Myke 2222 9876
...
I want to overwrite the 558 number in the file. The ID (1,2...) are given. I've been searching around and the fopen() function only provides arguments for beginning and appending.
You can use the fopen("filename", "r+b") to open a file for updating (in binary mode).
You can then use fseek to set were you want to write next and use fwrite/fputs or similar to update the data.
For example, say you start with a file test.text which looks like this
0123456789abcde
0123456789abcde
0123456789abcde
you can use the following program
#include <stdio.h>
int main(int argc, char **argv)
{
FILE *f;
if(!(f = fopen("./test.text", "r+b"))) //open the file for updating
return -1;
fseek(f, 16, SEEK_SET);//set the stream pointer 16 bytes from the start.
const char message[] = "Hello, World!";
fputs(message,f);
return 0;
}
and the result would be
0123456789abcde
Hello, World!de
0123456789abcde
you will still need to first search the file for the id you want to replace, and you may run into problems when you want to replace "558" with "12345" unless there are extra spaces on the line to pad the output. As you will start overwriting data from the next line.
fopen to open the file, then seek to the right bit, then write the appropriate bits, taking great care not to overwrite anything further in the file you didn't want to overwrite!
If you need to insert more data than was taken up by the existing text then you'll have to re-append the rest of the file contents from that point.
It is usually easier to re-write the file in its entirety. You must have a reading and a writing function already. So you read, the modify your in-memory data structures, then you write everything out again.
There are functions to seek to a given position in a file, but this position is a byte offset in the file. You do not know the offset in advance, so you will need to read the file line per line until you discover the line with the given ID. Then you can skip the name, the first number and you are at the 558. The problem now is that you can overwrite the 558 in the file, but you cannot write 12 and delete the 8 from 558. You also cannot replace the 558 with a 1234 because you would overwrite the 558 with 123 and would need to insert the 4.
One simple solution would be to work with a second file. You could read file 1 line per line and copy each line to file 2. Despite the line with the given ID. You would modify that line before writing it to file 2.
If you know how to do it, you could also work with a big buffer and do the replacement in file 1 without the need for a second file.
If you are allowed to work with two files, you should do that. If the files are short and you are allowed to read the complete file into memory, you could read the file, do the modification in memory and write back the file.
(Assuming your .txt file consists of the above mentioned 4 fields) You can have two FILE pointers one with read permissions and the other with write permission. Suppose your file is named xyz.txt, let fp_read with read access point to it, where let fp_write point to a new file xyz_bkp.txt.
Variables:
char *curr_line, *name;
int id, second_num, third_num;
The trick here is to read the file xyz.txt line by line into a string(say curr_line)
Then read the values in the line into different variables using sscanf. Eg-
sscanf(curr_line, "%d %s %d %d", &id, name, &second_num, &third_num);
Now based on values of the variables, either write the values to the xyz_bkp.txt file unconditionally or change them as per your will.
NOTE: After you are done, you would need to delete the original xyz.txt and rename xyz_bkp.txt to xyz.txt(simple enough)

Rewrite lines on file

I need to rewrite a char on a file, or delete some lines. Is there a way to achieve this without rewriting the whole file?
Example: I need to change the char "8" at line 10 with char "4".
pollo
ciao fred
98/98/34 42ddw
4
10
1234567890
cristo
ciao liby
98/98/34 fre42ddw
8
20
12345678901234567890
Look at
int fseek (stream, offset, origin);
You can move to a specific offset and write a symbol there. But to find an offset of char to replace you still need to read all symbols before it.
In a nutshell, yes, you can modify data at arbitrary positions in the file using random-access API methods. Of course, how the OS and filesystem handle this behind the scenes may result in the entire file being rewritten anyways.
You can use fseek and fputc, if you know exactly the position of the char. If not, you should better first fread the file and find the needed position. For other utility functions, see <stdio.h>.
Note that <stdio.h> is byte-based, rather than line-based. With line-based methods you would basically need to rewrite the file.
For deleting line from the file, well, you can just transfer all the characters from the positions i + [end of the line to be deleted] + 1 to positions i + [start of the line to be deleted]. Or read the whole into a buffer and manipulate the characters there. But for such a task, line-based functions are more appropriate.

Resources