1

编写一个 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 字符的文本文件有关。想知道是否有更好的方法来简单地覆盖与用户搜索匹配的行,或者至少将光标重置到与搜索匹配的行的开头。我在这个网站上做了一堆谷歌搜索和搜索,但我找不到任何我理解如何实现的东西。非常感谢您的帮助!干杯

4

2 回答 2

1

我有 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 文件中的长度相同时,您的代码才能正常工作。

于 2013-03-31T00:54:28.657 回答
0

您必须在给定文件流的读取和写入之间进行定位操作。

ISO/IEC 9899:2011 Section 7.21.5.3fopen功能

¶7 当文件以更新模式打开时(' +' 作为上述模式参数值列表中的第二个或第三个字符),可以在关联的流上执行输入和输出。但是,在没有对fflush函数或文件定位函数(fseekfsetposrewind输入操作遇到文件结尾。

您的代码必须执行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 上。I/O 系统在文本文件上执行 CRLF 到 NL 映射(这在 Windows 上是一个问题),这是限制的原因之一。

于 2013-03-31T01:03:42.873 回答