Программа телефонной книги C: переместите курсор в текстовый файл в начало предыдущей строки

Написание программы на C, которая открывает текстовый файл phoneList.txt и ищет контакты (имя, фамилия, номер телефона) и обновляет телефонный номер существующего контакта. Моя проблема в программе обновления телефонных номеров. Когда я использую fgets для поиска подходящего имени для обновления контакта, курсор располагается в начале следующей строки, то есть в начале контакта ПОСЛЕ контакта, который соответствует поиску пользователя. Вот мой код:

#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;
}

Я прокомментировал fseek, поскольку он ведет себя странно, в зависимости от того, находится ли искомый контакт в конце списка. Я думаю, это связано с тем, что в текстовом файле есть символ \ n. Интересно, есть ли лучший способ просто перезаписать строку, которая соответствует поиску пользователя, или, по крайней мере, сбросить курсор на начало строки, которая соответствует поиску. Я провел множество поисков и поисков в Google на этом сайте, но я не смог найти ничего, что я понял, как реализовать. Очень ценю вашу помощь! ура

2 ответа

К

Я прокомментировал fseek, поскольку он ведет себя странно, в зависимости от того, находится ли искомый контакт в конце списка.

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

В последней строке вы должны вернуть strlen(phoneInfo), а не strlen(phoneInfo)+1.

Потому что в других строках есть символ новой строки. Но в последней строке не может быть. Вы можете изменить его следующим менее эффективным способом:

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

Кстати, ваш код работает хорошо, только если длина phoneInfo такая же, как в файле txt.

Вы должны выполнить операцию позиционирования между операциями чтения и записи в указанном файловом потоке.

ISO / IEC 9899: 2011, раздел 7.21.5.3. fopen функция

When7 Когда файл открывается в режиме обновления ('+'как второй или третий символ в приведенном выше списке значений аргументов режима), и ввод, и вывод могут выполняться в связанном потоке. Однако за выводом не должен следовать непосредственно ввод без промежуточного вызова fflush функция или функция позиционирования файла (fseek, fsetpos, или же rewind), и за вводом не должен следовать непосредственно вывод без промежуточного вызова функции позиционирования файла, если только операция ввода не сталкивается с концом файла.

Ваш код должен сделать fseek() что-то вроде переместить файл для записи в любом случае; это также должно сделать другое fseek() после написания, прежде чем продолжать читать больше. Вам, вероятно, нужно следить за тем, откуда читается строка, прежде чем читать ее (ftell(), fseek() или же fsetpos()), так что вы можете найти начало строки снова.

Также дано:

char line[100];

ваш звонок в fgets():

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

безопасно, но вы могли бы одинаково безопасно использовать:

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

и использовать все пространство, которое вы выделили, а не только 99%. Кроме того, если вы измените длину строки с 100 на 256 или 1024 или 4096, вам нужно только изменить объявление - не там, где используется переменная.

В том же духе у вас есть:

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

Вы не выделяли место для stream указать на; вы пишете и случайные места в вашей программе.

    //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);

это sscanf() Обновление должно быть различными способами:

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

Это обеспечивает правильную длину для полей структуры (обратите внимание, что ваши переменные используют 20 и 15, но форматы должны использовать 19 и 14 - этот сдвиг по одному также является источником переполнения), и он проверяет, что преобразования все были успешны.

Кстати, новый телефонный номер можно записать поверх старого только в том случае, если новая строка и старая строка имеют одинаковую длину. Если новая строка короче, вам нужно заполнить ее старой длиной, чтобы убрать последние несколько цифр старого номера. Если новая строка длиннее, у вас есть серьезные проблемы; вы перезапишете начало следующей записи. Вы должны перемешать содержимое файла вместе с соответствующим количеством байтов (разница в длине между старыми и новыми записями). См. SO 10467711 для кода, который может вставлять данные в середину файла.

Также имейте в виду, что официально для текстового файла больше ограничений, чем для двоичных файлов. Однако многие из них скорее теоретические, чем практические, особенно в Unix. Система ввода / вывода выполняет сопоставление CRLF и NL для текстовых файлов (что является проблемой в Windows), что является одной из причин ограничений.

Другие вопросы по тегам