文件锁定+;Fscanf/Lseek

文件锁定+;Fscanf/Lseek,c,locking,file-descriptor,lseek,C,Locking,File Descriptor,Lseek,我有一个名为“input.txt”的文件,其中包含一些值。我正在编写一个程序,该程序将在该文件中找到最小值,并将该最小值替换为作为命令行参数给出的数字——如果该命令行参数大于最小值。这些值表示室温,因此可以使用事实来确定最低温度。此外,需要锁定文件的该部分(新编号替换最小值) 示例: $ ./prog 23 档案:2125192224 档案:2125232224 档案:2125232224 档案:2925232224 代码: #include <stdio.h> #include

我有一个名为“input.txt”的文件,其中包含一些值。我正在编写一个程序,该程序将在该文件中找到最小值,并将该最小值替换为作为命令行参数给出的数字——如果该命令行参数大于最小值。这些值表示室温,因此可以使用事实来确定最低温度。此外,需要锁定文件的该部分(新编号替换最小值)

示例:

$ ./prog 23
档案:2125192224

档案:2125232224

档案:2125232224

档案:2925232224

代码:

#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;
}
正如你所看到的,这是完全相同的概念。第二个程序有效,第一个程序无效


我现在很困惑。若我从文件流创建文件描述符,然后在该文件描述符上使用
lseek()
来更改偏移量,那个么流的偏移量也会更改吗?如果您使用
fscanf()
从流中读取某些内容,那么
offset\u t
的更改是否与从文件中读取的内容相同?如果我将
fscanf()
与格式说明符
%d
%s
一起使用,在更改偏移量方面是否有任何区别

只有当两个(源文本和替换文本)在您的case length(aa)=length(bb)中具有相同的长度时,您想要替换值的方式才会起作用。主要是您应该小心使用
文件*
int fd
描述符,并始终在退出之前关闭文件

fclose(f)
之前调用
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
}
Update1
文件
接口有自己的缓冲区,与
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);
}

您修改了已经运行的代码。第一个代码就是问题所在。我不明白为什么它不起作用,而第二个却起作用。“只有当两个(源文本和替换文本)的长度相同时,替换值的方法才会起作用。”-这根本不是真的,我可以锁定整个文件(如果我想的话)并替换其中的一小部分。如果文件内容“ab cd ef”将“cd”替换为“xyz”,会发生什么?我的意思是内容将是腐败者。如果替换代码的长度不是源代码,那么您必须将文件重写到最后。正如我所说的,我了解第二个代码的作用,以及它何时起作用,何时不起作用。假设您可以用另一个两个字母的单词替换两个字母的单词。第二个代码可以工作,因为您没有关闭
fdOpen
。当您添加这样的数据时(如您在第一个代码中所做的),行为将是相同的-文件不会更改,因为使用
fprintf
写入的数据实际上在
fflush
fclose
之前不会写入。请指定数字(室温)的约束条件,它们是否始终为2位正整数?这可以大大简化问题。文件中只有一个最小值。如果存在多个最小值,请指定场景。是否用fseek替换了第一个lseek?在那里使用lseek时,不清除第一个循环中的EOF似乎是个问题。
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
}
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);
}