-1

Please help me, I made a function to modify a line in file using C language. Now my problem is that although the line is modified, the rest of the line I modified is still as is; For example if the new data I enter is shorter than the original line, my new data will be written, but the rest of the line will still be filled with old data. Please check the first line in the outputs below. Please advise me, thank you very much.

Original file:

Sam,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas@gmail.com
Steven,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas@gmail.com

Expected output file:

John,George,12-1-2000,23 MN Street,1234567899,gJoh@gmail.com
Steven,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas@gmail.com

Real output file:

John,George,12-1-2000,23 MN Street,1234567899,gJoh@gmail.com@gmail.com
Steven,Thomas,10-06-1995,26 Elhoreya Street,01234567899,sthomas@gmail.com

This is my function:

void modify(contact c[])
{
    int flag=0, k=0, count_matches=0, match_cont_pos[100], choose;
    char name[50], temp;

    FILE * file;
    file = fopen("info.txt", "rb+");

    if(file == NULL)
    {
        printf("Error opening file\n");
        exit(1);
    }

    else
    {
        printf("\nEnter the name you want to search: ");
        scanf("%s",&name);

        for(int j = 0; name[j] != '\0'; j++)
            name[j] = tolower(name[j]);

        for(int i = 0; i < count ; i++) {

            for(int j = 0; c[i].last_name[j] != '\0'; j++)
                c[i].last_name[j] = tolower(c[i].last_name[j]);

            if(strcmp(c[i].last_name,name) == 0)
            {
                match_cont_pos[k]=i;
                k++;
            }

        }
        for(int d=0;d<k;d++)
            printf("match pos %d\n",match_cont_pos[d]);

        for(int d=0;d<k;d++){
            printf("\n%s\n%s\n%d %d %d\n%s\n%d\n%s\n\n", c[match_cont_pos[d]].last_name,c[match_cont_pos[d]].first_name,c[match_cont_pos[d]].DoB.day,c[match_cont_pos[d]].DoB.month,c[match_cont_pos[d]].DoB.year,c[match_cont_pos[d]].street_address,c[match_cont_pos[d]].phone_number,c[match_cont_pos[d]].email);
        }

        if(k>1){

            printf("Enter the number of the record you want to modify: ");
            scanf("%d",choose);

            printf("Enter last name: ");
            scanf("%s",&c[match_cont_pos[choose-1]].last_name);
            printf("Enter first name: ");
            scanf("%s",&c[match_cont_pos[choose-1]].first_name);
            printf("Enter phone number: ");
            scanf("%d",&c[match_cont_pos[choose-1]].phone_number);
            printf("Enter email: ");
            scanf("%s",&c[match_cont_pos[choose-1]].email);
            printf("Enter birth date: ");
            scanf("%d %d %d",&c[match_cont_pos[choose-1]].DoB.day,&c[match_cont_pos[choose-1]].DoB.month,&c[match_cont_pos[choose-1]].DoB.year);
            printf("Enter the address: ");
            scanf("%c",&temp); // temp statement to clear buffer
            scanf("%[^\n]",&c[match_cont_pos[choose-1]].street_address);

            fseek(file,-sizeof(c),SEEK_SET);

            fprintf(file,"%s,%s,%d-%d-%d,%s,%d,%s",c[match_cont_pos[choose-1]].last_name,c[match_cont_pos[choose-1]].first_name,c[match_cont_pos[choose-1]].DoB.day,c[match_cont_pos[choose-1]].DoB.month,c[match_cont_pos[choose-1]].DoB.year,c[match_cont_pos[choose-1]].street_address,c[match_cont_pos[choose-1]].phone_number,c[match_cont_pos[choose-1]].email);
        }

    }
    fclose(file);
}

Hagar
  • 37
  • 6
  • `-sizeof(c)`. That won't work. `sizeof` does not work for arrays passed to functions. The array decays to a pointer and `sizeof` a pointer is a fixed 4 or 8 bytes (depending on the platform pointer size). – kaylum Jan 22 '21 at 00:05
  • 3
    You _can't_ change the length of a line of text in a text file _in-place_ [where the lines are of varying length]. You have to have _two_ files (one is a temp). You open the original for reading and open the temp for writing. loop on (e.g.) `fgets` on the original, change the buffer of the line you want, and do (e.g.) `fputs` to the temp. When done, you can do (e.g.) `rename(temp,origfile);` – Craig Estey Jan 22 '21 at 00:05
  • yeah, I get it now, thank you very much, I will do that then @CraigEstey – Hagar Jan 22 '21 at 00:09
  • @kaylum thank you very much for your advice, can you tell me what do i use instead of sizeof() ? – Hagar Jan 22 '21 at 00:10
  • Craig is basically correct. You can do insert or delete "in place", but it involves a lot of fiddly (and careful) copying. And for shrinking a file, you still end up using `ftruncate()` or `truncate()`. See also [Write in the middle of a binary file without overwriting any existing content](https://stackoverflow.com/q/10467711/15168) and [Delete part of a file in C](https://stackoverflow.com/q/50996698/15168). – Jonathan Leffler Jan 22 '21 at 00:10
  • ok great, thank you very much @JonathanLeffler – Hagar Jan 22 '21 at 00:12
  • Jonathan is basically correct. You _can_ do in-place with a lot of fiddling. But, unless you're locking the file with (e.g.) `flock`, another program may see the _partially_ modified contents (the contents of the file in the middle of the fiddle). `rename` is _atomic_. Another program will see either the _original_ contents or the _modified_ contents, but _never_ a mishmash of the two. So, the read/modify/rename is probably better in most cases. – Craig Estey Jan 22 '21 at 00:19
  • Note that if you had _fixed_ length _binary_ data (e.g. `struct record { char first_name[40]; char last_name[40]; char street[100]; ... };`, you could `lseek/fseek` the file and do in-place nicely [and quickly]. Doing this in conjunction with `flock` is quite doable. – Craig Estey Jan 22 '21 at 00:21
  • I see, thank you very very much @CraigEstey. Can you please advice me one last thing, can you tell me a way to move along the lines in the file? because fseek() is moving by letters, is there other ways to move by lines? thank you – Hagar Jan 22 '21 at 00:26

2 Answers2

0

Consider these suggestions, they are to lead you towards an answer:

  • Your file is like a byte array, modifying some bytes in the middle does not change the position of the remaining bytes in the file.
  • You may be overwriting part of one line and leaving extra characters from the old line in place if the new line is shorter than the old one.
    You might be overwriting part of the next line if the new line is longer than the old line
  • The code searches the 'C[]' array and not the file, is the array and file guaranteed to be in sync? Are you searching the array or the file?
  • The line " for(int i = 0; i < count ; i++)" has a variable 'Count'. Where is 'Count' defined?
snj
  • 72
  • 8
  • Thank you very much for answering, 1- Yes I search the array then print in the file. 2- count is defined as a global variable there are other parts of the code that I did not attach – Hagar Jan 22 '21 at 00:16
0

Can you please advice me one last thing, can you tell me a way to move along the lines in the file? because fseek() is moving by letters, is there other ways to move through the lines?

There's no shortcut with an ordinary text file [without some organization].

It's [effectively] just a long string of bytes. If your file was (e.g):

Brown,John,123 Elm St\n
FogginFoodle,Cynthia;36277 North Hollywood Avenue\n
Mertz,Fred,7003 Fifth Ave Apt 6\n

There's no way to "seek by newline". We have to read sequentially.

But ...

There are some approximate tricks.


For example, we could read the file sequentially once, and have an array of offsets of where the newlines are.


If we only write/modify/update occasionally, we could open the file for readonly, and mmap the [entire] file and use fast string functions (e.g. strchr) to find newlines within the mapped area of main memory.

See my answers:

  1. Copying the lines from a file to another backwards in c
  2. read line by line in the most efficient way *platform specific*
  3. How does mmap improve file reading speed?

The following is pretty much a waste unless we're using the mmap technique.

If the file is large enough [to make this complexity worthwhile], and is sorted by some field (e.g.) last name ...

We have/get the length of the file. We can do a binary search [sort of].

Go to the midpoint position. This is just changing a char * pointer that is within the mapped memory area.

The [rough] midpoint we chose, may be [probably will be] in the middle of a line. So, just go char-by-char forward/backward [again, by incrementing/decrementing the char * pointer], stopping on the next/previous newline. Now, we can look at the field just after a given newline. This will be the last name.

We can compare against our "desired" last name, and adjust the midpoint of the binary search up/down. We repeat the above steps until we find the record we're looking for.


Unless we have a huge amount of data, this may be overkill. And, if we do have a large amount of data, we probably want the "fixed record size" approach I mentioned in my top comment (i.e.) we have a database of sorts.

It is very fast for scanning and record update.

It suffers from the same issue that the text file had if we're trying to insert/delete records. We'd have to [as Jonathan would say] "fiddle". But, with mmap approach the fiddling is just a memcpy in conjunction with ftruncate [and possible mmap/mremap]. But, we're still having to copy and we need to issue lock to prevent others from accessing the file while we're changing it.

That's why companies make money on selling database software ;-)


A simpler technique may be to split up the large file. Create a directory. Put all records that have a last name starting with "A" in the A file in the directory, "B" in B, ...

Craig Estey
  • 30,627
  • 4
  • 24
  • 48