3

いくつかの値を保持する「input.txt」というファイルがあります。そのファイルで最小値を見つけ、その最小値をコマンドライン引数として指定された数値に置き換えるプログラムを書いています-そのコマンドライン引数が最小値よりも大きい場合。これらの値は室温を表すため、事実を使用して最小値を見つけることができます。さらに、ファイルのその部分 (新しい番号が最小値に置き換わる場所) をロックする必要があります。

例:

$ ./prog 23

ファイル: 21 25 19 22 24

ファイル: 21 25 23 22 24

$ ./prog 29

ファイル: 21 25 23 22 24

ファイル: 29 25 23 22 24

コード:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdint.h>

/* Function that handles errors */
void fatal_error(const char *message){

    perror(message);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv){

    if(argc != 2)
        fatal_error("Bad arguments.\n");

    /* Fetching command line argument */
    int temp = atoi(argv[1]);

    /* Opening file and checking for errors */
    FILE *file = fopen("input.txt", "r+");
    if(!file)
        fatal_error("Unable to open file.\n");

    /* Finding minimum in the file */
    int min = 200;
    int value;
    while(fscanf(file, "%d", &value) != EOF)
        if(value < min)
            min = value;

    /* Exiting if nothing needs to change */
    if(temp <= min)
        return 0;

    /* Creating file descriptor from stream and checking for errors */
    int fdOpen = fileno(file); 
    if(fdOpen == -1)
        fatal_error("Unable to open file descriptor.\n");

    /* Moving offset to the beginning of the file */
    off_t of = lseek(fdOpen,0,SEEK_SET);
    printf("Ofset pre petlje: %jd\n", (intmax_t)of);
    while(1){

        /* I'm reading file all over again */
        if(fscanf(file, "%d", &value) == EOF)
            fatal_error("Reached end of file.\n");

        /* If I reached minimum */
        if(value ==  min){

            /* I lock that part of the file - temperatures are two digit numbers*/
            struct flock lock;
            lock.l_type = F_WRLCK;
            lock.l_whence = SEEK_CUR;
            lock.l_start = -2;
            lock.l_len = 2;

            /* I create lock */
            if(fcntl(fdOpen,F_SETLK, &lock) == -1){

                if(errno == EACCES || errno == EAGAIN)
                    fatal_error("File is locked.\n");
            }

            /* Moving two positions back from current position */
            off_t offset;
            if((offset = lseek(fdOpen, -2, SEEK_CUR)) == -1)
                fatal_error("lseek error.\n");

            /* Inserting read command line value into file */
            fprintf(file, "%d", temp);

            /* Unlocking */
            lock.l_type = F_UNLCK;
            if(fcntl(fdOpen, F_SETLK, &lock) == -1)
                fatal_error("Unable to destroy lock.\n");

            /* Closing file descriptor, and breaking loop */
            close(fdOpen);
            break;
        }
    }
    return 0;
}

ただし、これは機能しません。ファイルは変更されません。問題は、これが正しい方法であることを知っていることです。私は基本的に、単語"aa"の出現ごとに"bb"に変更する同じプログラムを作成しました。基本的に同じコンセプトです。

私はもう試した:

  • ループの前後にオフセットをフェッチしようとしましfscanf()た 。while()最初のfscanfオフセットが 0 に設定される前 - ファイルの先頭。最初のfscanfオフセットがファイルの最後に設定された後、各反復の後、オフセットはファイルの最後に残ります。
  • ftell()これらと同じものを使用しprintf()てみましたが、ftell() 正しいオフセットが得られます。ただしftell()、 while ループではまだファイルの終わりが返されます。私もfseek()代わりに 使用してみました(実装での使用lseek()を知っていても)。それでも同じ結果。fseek()lseek()
  • char*値の代わりにバッファを使用してみました。基本的には fscanf(file,"%s",buffer)、その値を変換して読み取り値が最小かどうかを確認し、その後、それ fprintf(file,"%s",buffer)を記述するのに使用しました。でも同じ結果。
  • ファイル全体をロックしようとしましたが(おそらく問題があると思いました)、同じ結果になりました。

コード:

これは、同じ概念を使用する、私が言及した2番目のプログラムです。このコードは機能しますが、ここでもオフセットを印刷しようとしましたが、オフセットもファイルの最後にあります。

int main(int argc, char **argv){

    if(argc != 4)
        fatal_error("You must enter exactly 4 arguments.\n");

    FILE *f = fopen(argv[1], "r+");
    if(!f)
        fatal_error("Unable to open file for reading and writing.\n");

    int fd = fileno(f);
    if(fd == -1)
        fatal_error("Unable to fetch file descriptor for file.\n");

    char word[MAX_LEN + 1];
    int word_len = strlen(argv[2]);
    while(fscanf(f,"%s",word) != EOF){

        printf("%jd\n", lseek(fd,0,SEEK_CUR));
        if(!strcmp(argv[2],word)){

            struct flock lock;
            lock.l_type = F_WRLCK;
            lock.l_whence = SEEK_CUR;
            lock.l_start = -word_len;
            lock.l_len = word_len;

            if(fcntl(fd, F_SETLKW, &lock) == -1)
                fatal_error("File locking failed.\n");

            if(lseek(fd, -word_len, SEEK_CUR) == -1)
                fatal_error("Lseek error.\n");
            fprintf(f, "%s", argv[3]);

            lock.l_type = F_UNLCK;
            if(fcntl(fd, F_SETLK, &lock) == -1)
                fatal_error("Failed to release lock.\n");
        }
    }
}

ご覧のとおり、まったく同じ概念です。2 番目のプログラムは機能しますが、最初のプログラムは機能しません。

私は今とても混乱しています。ファイルストリームからファイル記述子を作成lseek()し、そのファイル記述子を使用してオフセットを変更すると、ストリームのオフセットも変更されますか? fscanf()また、ストリームから何かを読み取るために使用する場合offset_t、ファイルから読み取るのと同じくらい変更しますか? fscanf()フォーマット指定子%dとを使用する場合、オフセットの変更に違いはあり%sますか?

4

1 に答える 1

1

値を置換する方法は、両方 (ソースと置換テキスト) が長さ (aa)==長さ (bb) の場合に同じ長さの場合にのみ機能します。FILE*主に、記述子の使用に注意し、int fd終了する前に常にファイルを閉じる必要があります。

close(fd)beforeを呼び出すfclose(f)と、バッファリングされたデータが書き込まれません。

もう 1 つの問題 - ファイル領域を SEEK_CUR に対して相対的にロックしても、変更したいファイルの部分がロックされない

ここでは、動作している少し変更されたコードがあります。

int main(int argc, char **argv){

    if(argc != 4)
        fatal_error("You must enter exactly 3 arguments.\n");

    if(strlen(argv[2])!=strlen(argv[3]))
        fatal_error("src&dst words must be the length.\n");

    FILE *f = fopen(argv[1], "r+");
    if(!f)
        fatal_error("Unable to open file for reading and writing.\n");

    int fd = fileno(f);
    if(fd == -1)
        fatal_error("Unable to fetch file descriptor for file.\n");

    char word[MAX_LEN + 1];
    int word_len = strlen(argv[2]);
    while(fscanf(f,"%s",word) != EOF){

        printf("%jd\n", ftell(f));
        if(!strcmp(argv[2],word)){

            struct flock lock;
            lock.l_type = F_WRLCK;
            lock.l_whence = SEEK_SET;
            lock.l_start = ftell(f)-word_len;
            lock.l_len = word_len;

            if(fcntl(fd, F_SETLKW, &lock) == -1)
                fatal_error("File locking failed.\n");

            fseek(f,-word_len,SEEK_CUR); //FILE* based seek
            fprintf(f, "%s", argv[3]);
            fflush(f); //sync output

            lock.l_type = F_UNLCK;
            if(fcntl(fd, F_SETLK, &lock) == -1)
                fatal_error("Failed to release lock.\n");
        }
    }
    fclose(f); // close the file
}

Update1 :FILEインターフェイスには独自のバッファリングがあり、 と同期していませんint fd。したがって、主な問題はlseekを使用することですが、fseekを使用する必要があります

Update2 : 最小値を探すループを含むコード

int main(int argc, char **argv){

    if(argc != 3)
        fatal_error("You must enter exactly 2 arguments.\n");

    if(strlen(argv[2]) != 2)
        fatal_error("replace num must have 2 digits.\n");

    FILE *f = fopen(argv[1], "r+");
    if(!f)
        fatal_error("Unable to open file for reading and writing.\n");

    int fd = fileno(f);
    if(fd == -1)
        fatal_error("Unable to fetch file descriptor for file.\n");

    // search for minimum
    int word_len = strlen(argv[2]);
    int value, minValue;
    long minOffs=-1;
    while(fscanf(f,"%d",&value) == 1){ //compare number of parsed items
        printf("test value %d\n", value);
        if (minValue > value) {
            minValue = value;
            minOffs = ftell(f) - word_len;
        }
    }

    // replace if found
    if (minOffs >= 0) {
        printf("replacing v=%d at %ld\n", minValue, minOffs);
        struct flock lock;
        memset(&lock, 0, sizeof(lock));
        lock.l_type = F_WRLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = minOffs;
        lock.l_len = word_len;

        fseek(f,minOffs,SEEK_SET);
        if(fcntl(fd, F_SETLK, &lock) == -1)
            fatal_error("File locking failed.\n");

        fprintf(f, "%s", argv[2]);
        fflush(f); //sync output

        lock.l_type = F_UNLCK;
        if(fcntl(fd, F_SETLK, &lock) == -1)
            fatal_error("Failed to release lock.\n");
    }
    fclose(f);
}
于 2016-06-20T16:03:19.733 に答える