文件锁定 + Fscanf/Lseek
File locking + Fscanf/Lseek
我有一个名为 "input.txt" 的文件,其中包含一些值。我正在编写程序,它将在该文件中找到最小值,并将该最小值替换为作为命令行参数给出的数字 - 如果该命令行参数大于最小值。这些值代表室温,因此可以使用该事实来找到最小值。此外,文件的那部分(新数字替换最小值的地方)需要被锁定。
示例:
$ ./prog 23
FILE: 21 25 19 22 24
FILE: 21 25 23 22 24
$ ./prog 29
FILE: 21 25 23 22 24
FILE: 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
偏移量设置为文件末尾后,
并且偏移量在每次迭代后保留在文件末尾。
- 我试过在
printf()
和 ftell()
中使用 ftell()
给我正确的偏移量。但是 while 循环中的 ftell()
仍然
给出文件结尾。我也试过使用 fseek()
而不是
lseek()
(即使我知道 fseek()
在其中使用 lseek()
执行)。还是一样的结果。
- 我试过使用
char*
缓冲区而不是值。基本上使用
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
的更改是否与您从文件中读取的一样多?如果我将 fscanf()
与格式说明符 %d
和 %s
一起使用,更改偏移量有什么不同吗?
当(源文本和替换文本)在您的情况下具有相同长度时,您想要替换值的方式将有效仅 lenght(aa)==length(bb) .主要是你应该小心使用 FILE*
和 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: 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);
}
我有一个名为 "input.txt" 的文件,其中包含一些值。我正在编写程序,它将在该文件中找到最小值,并将该最小值替换为作为命令行参数给出的数字 - 如果该命令行参数大于最小值。这些值代表室温,因此可以使用该事实来找到最小值。此外,文件的那部分(新数字替换最小值的地方)需要被锁定。
示例:
$ ./prog 23
FILE: 21 25 19 22 24
FILE: 21 25 23 22 24
$ ./prog 29
FILE: 21 25 23 22 24
FILE: 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
偏移量设置为文件末尾后, 并且偏移量在每次迭代后保留在文件末尾。 - 我试过在
printf()
和ftell()
中使用ftell()
给我正确的偏移量。但是 while 循环中的ftell()
仍然 给出文件结尾。我也试过使用fseek()
而不是lseek()
(即使我知道fseek()
在其中使用lseek()
执行)。还是一样的结果。 - 我试过使用
char*
缓冲区而不是值。基本上使用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
的更改是否与您从文件中读取的一样多?如果我将 fscanf()
与格式说明符 %d
和 %s
一起使用,更改偏移量有什么不同吗?
当(源文本和替换文本)在您的情况下具有相同长度时,您想要替换值的方式将有效仅 lenght(aa)==length(bb) .主要是你应该小心使用 FILE*
和 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: 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);
}