user1730099
user1730099

Reputation: 81

C phonebook program: Reposition cursor in text file to start of previous line

writing a C program which opens a text file called phoneList.txt and searches for contacts (firstname, lastname, phonenumber), and updates an existing contact's phone number. My issue is in the update phone number program. When I use fgets to find a matching name to update the contact, the cursor is positioned at the start of the next line, aka at the start of the contact AFTER the contact that matches the user's search. Heres my code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(void){
struct record{
        char firstName[20];
        char lastName[20];
        char phoneNum[15];
    };
    struct record info;
    FILE *fp;
    char line[100];
    char phoneInfo[100];
    char fullName[100];
    char *stream;
    int n = atoi(getenv("CONTENT_LENGTH"));
    fgets(stream, n+1, stdin);  //put query string into stream from stdin
        //SET HTML OUTPUT AND TITLE, TEST CONTENT OF STREAM
    printf("%s%c%c\n", "Content-type:text/html;charset=iso-8859-1",13,10);
    printf("<p>%s</p>", stream);
    sscanf(stream, "firstName=%[^&]&lastName=%[^&]&phoneNum=%s", info.firstName, info.lastName, info.phoneNum);
    strcpy(fullName, info.firstName);
    strcat(fullName, " ");
    strcat(fullName, info.lastName);
    strcpy(phoneInfo, fullName);
    strcat(phoneInfo, " ");
    strcat(phoneInfo, info.phoneNum);
    //strcat(phoneInfo, "\n");
    printf("%s", phoneInfo);
    printf("\n");
        //TEST FORMATTING OF PHONE INFO VAR
    fp = fopen("phoneList.txt", "r+");
    if(fp == NULL){
        printf("Error opening the file.\n");
        return 1;
    }
    while(fgets(line, 99, fp)!=NULL){
        if(strstr(line, fullName)!= NULL){
            //fseek(fp, -(strlen(phoneInfo)+1), SEEK_CUR);
            fputs(phoneInfo, fp);
            printf("Success! Number updated. \n");
            fclose(fp);
            return;
        }
    }
    if(feof(fp)){
        //fputs(phoneInfo, fp);
        printf("goes to here");
    }
    fclose(fp);
    return 0;
}

I have fseek commented as it behaves strangely depending on if the contact being searched is at the end of the list. I think it has something to do with the text file having a \n char in it. Wondering if there is a better way to simply overwrite the line which matches the user's search, or at least reset the cursor to the start of the line that matches the search. Ive done a bunch of google searching and searches on this site but I couldnt turn up anything that I understood how to implement. Really appreciate your help! Cheers

Upvotes: 1

Views: 1445

Answers (2)

Sheng
Sheng

Reputation: 3555

TO

I have fseek commented as it behaves strangely depending on if the contact being searched is at the end of the list.

fseek(fp, -(strlen(phoneInfo)+1), SEEK_CUR);

At the last line, you should back strlen(phoneInfo), rather than strlen(phoneInfo)+1.

Because in the other lines, there is a newline character. But in the last line, there might not be. You might modify it in following less-efficient way:

if(line[strlen(line)-1]=="\n"){
    fseek(fp, -(strlen(phoneInfo)+1), SEEK_CUR);
}
else{
    fseek(fp, -strlen(phoneInfo), SEEK_CUR);
}

By the way, your code only works well if the length of phoneInfo is the same as these in the txt file.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 753725

You must do a positioning operation between reads and writes on the given file stream.

ISO/IEC 9899:2011 Section 7.21.5.3 The fopen function

¶7 When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of- file.

Your code must do an fseek() of some sort to reposition the file for writing anyway; it must also do another fseek() after writing before continuing to read more. You probably need to keep a track of where the line is read from before you read it (ftell(), fseek() or fsetpos()) so you can locate the start of the line again.

Also, given:

char line[100];

your call to fgets():

while (fgets(line, 99, fp)!=NULL){

is safe, but you could equally safely use:

while (fgets(line, sizeof(line), fp) != NULL)

and use all the space that you've allocated, rather than just 99% of it. Also, if you change the line length from 100 to 256 or 1024 or 4096, you only have to change the declaration — not where the variable is used.

In a similar vein, you have:

struct record
{
    char firstName[20];
    char lastName[20];
    char phoneNum[15];
};
struct record info;
FILE *fp;
char line[100];
char phoneInfo[100];
char fullName[100];
char *stream;

int n = atoi(getenv("CONTENT_LENGTH"));
fgets(stream, n+1, stdin);  //put query string into stream from stdin

You didn't allocate space for stream to point to; you are writing and random locations in your program.

    //SET HTML OUTPUT AND TITLE, TEST CONTENT OF STREAM
printf("%s%c%c\n", "Content-type:text/html;charset=iso-8859-1",13,10);
printf("<p>%s</p>", stream);

sscanf(stream, "firstName=%[^&]&lastName=%[^&]&phoneNum=%s",
       info.firstName, info.lastName, info.phoneNum);

This sscanf() should be upgrade in various ways:

if (sscanf(stream, "firstName=%19[^&]&lastName=%19[^&]&phoneNum=%14s",
           info.firstName, info.lastName, info.phoneNum) != 3)
{
    ...sscanf failed...
}

This enforces the correct lengths for the fields of the structure (note that your variables use 20 and 15, but the formats must use 19 and 14 — this off-by-one is a source of overflows too), and it checks that the conversions were all successful.

Incidentally, it is only safe to write the new phone number over the old if the new string and the old string are the same length. If the new string is shorter, you need to blank pad it to the old length to zap the last few digits of the old number. If the new string is longer, you've got major problems; you'll overwrite the start of the next entry. You have to shuffle the contents of the file along by an appropriate number of bytes (the difference in length between the old and the new entries). See SO 10467711 for code that can insert data into the middle of a file.

Also be aware that there are officially more restrictions on a text file than on binary files. However, many of those are more theoretical than practical, especially on Unix. The I/O system does CRLF to NL mapping on text files (which is an issue on Windows), which is one reason for constraints.

Upvotes: 0

Related Questions