Программа телефонной книги 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), что является одной из причин ограничений.