1

phoneList.txt というテキスト ファイルを開いて連絡先 (名、姓、電話番号) を検索し、既存の連絡先の電話番号を更新する C プログラムを作成します。私の問題は、電話番号の更新プログラムにあります。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)+1 ではなく、strlen(phoneInfo) を戻す必要があります。

他の行には改行文字があるためです。しかし、最後の行には、ないかもしれません。次の効率の悪い方法で変更することもできます。

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 セクション 7.21.5.3fopen関数

¶7 ファイルが更新モード (+上記のモード引数値のリストの 2 番目または 3 番目の文字として ' ') で開かれると、入力と出力の両方が関連付けられたストリームで実行される場合があります。ただし、fflush関数またはファイル配置関数 ( fseekfsetpos、または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 ではそうです。I/O システムは、テキスト ファイルで CRLF から NL へのマッピングを行います (これは Windows の問題です)。これが制約の理由の 1 つです。

于 2013-03-31T01:03:42.873 に答える