簡體   English   中英

文件鎖定+ Fscanf / Lseek

[英]File locking + Fscanf/Lseek

我有一個名為“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” 它基本上是相同的概念。

我試過了:

  • 我嘗試在while()循環中fscanf()之前和之后獲取偏移量。 在第一個fscanf偏移設置為0之前 - 文件的開頭。 首先將fscanf offset設置為文件的末尾,並在每次迭代后將offset保留在文件的末尾。
  • 我嘗試在這些相同的printf()使用ftell()ftell()給出了正確的偏移量。 但是,while循環中的ftell()仍然給出了文件的結尾。 我也嘗試使用fseek()而不是lseek() (即使我知道fseek() lseek()在其實現中使用lseek() )。 仍然,同樣的結果。
  • 我嘗試使用char* buffer而不是值。 基本上使用fscanf(file,"%s",buffer) ,轉換該值以檢查讀取值是否最小,然后我使用fprintf(file,"%s",buffer)來編寫它。 然而同樣的結果。
  • 我試過鎖定整個文件(我想 - 也許有問題),但是,同樣的結果。

碼:

這是我提到的第二個使用相同概念的程序。 這段代碼有效,但我也嘗試過打印偏移量,偏移量也在文件的末尾。

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");
        }
    }
}

正如您所看到的那樣,它是完全相同的概念。 第二個程序有效,第一個沒有。

我現在很困惑。 如果我從文件流創建文件描述符,然后在該文件描述符上使用lseek()來改變偏移量,流的偏移量是否也會改變? 如果您使用fscanf()從流中讀取內容,那么offset_t更改與您從文件中讀取的內容一樣多嗎? 如果我使用格式說明符%d%s fscanf() ,改變偏移量是否有任何區別?

只有當(源文本和替換文本)具有相同的長度(例如,leaht(aa)== length(bb))時,您想要替換值的方式才有效。 主要是你應該小心使用FILE*int fd描述符,並在退出之前始終關閉文件。

fclose(f) close(fd)之前調用close(fd)將導致不寫入緩沖數據。

另一個問題 - 相對於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
}

Update1FILE接口有自己的緩沖,與int fd不同步。 所以主要的問題是使用l​​seek,而應該使用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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM