我试着逐行读取和解析一个文件。我只想使用简单的系统(read,open,close,.)而不是fgets或getc,因为我希望在某种程度上学习基本面。(我看了一些类似问题的答案,但他们都使用fgets之类的)。
我现在拥有的是:我编写的一个函数,它可以从文件中将1024个字符存储在缓冲区中。
int main(void) {
const char *filename = "file.txt";
int fd = open(filename, O_RDONLY);
char *buffer = malloc(sizeof (char) * 1024);
read(fd, buffer, 1024);
printf("%s", buffer);
close(fd);
free(buffer);
}例如,如何在“\n”处停车?我知道,一旦我知道在哪里停止,我可以使用lseek与正确的偏移继续读取我的文件,我停止了。
我不希望将整个文件存储在我的缓冲区中,然后解析它。我想在缓冲区中添加一行,然后解析该行并重新分配缓冲区并继续读取文件。
我当时正在考虑这样的事情,但我觉得它优化得很糟糕,之后不知道该在哪里添加lseek:
char *line = malloc(sizeof (char) * 1024);
read(fd, buffer, 1);
int i = 0;
while(*buffer != '\n' && *buffer != '\0'){
line[i] = *buffer;
++i;
*buffer++;
read(fd, buffer, 1); //Assuming i < 1024 and *buffer != NULL
}
/* lseek somewhere after, probably should make 2 for loops
** One loop till file isn't completly read
** Another loop inside that checks if the end of the line is reached
** At the end of second loop lseek to where we left
*/谢谢:)
编辑:标题澄清。
发布于 2015-10-13 15:29:56
如果要使用read一次读取一行( fgets或getline打算做什么),则必须在找到每个'\n'之后跟踪文件中的偏移量。然后,只需一次读取一行,在当前的偏移量处开始下一个read。
我理解想要能够使用低级别的函数以及fgets和getline。您所发现的是,您基本上以重新编码(以一种效率较低的方式)结束了已经在fgets和getline中完成的工作。但这当然是很好的学习。下面是一个简短的例子:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSZ 128
ssize_t readline (char *buf, size_t sz, char *fn, off_t *offset);
int main (int argc, char **argv) {
if (argc < 2) return 1;
char line[BUFSZ] = {0};
off_t offset = 0;
ssize_t len = 0;
size_t i = 0;
/* using open/read, read each line in file into 'line' */
while ((len = readline (line, BUFSZ, argv[1], &offset)) != -1)
printf (" line[%2zu] : %s (%zd chars)\n", i++, line, len);
return 0;
}
/* read 'sz' bytes from file 'fn' beginning at file 'offset'
storing all chars in 'buf', where 'buf' is terminated at
the first newline found. On success, returns number of
characters read, -1 on error or EOF with 0 chars read.
*/
ssize_t readline (char *buf, size_t sz, char *fn, off_t *offset)
{
int fd = open (fn, O_RDONLY);
if (fd == -1) {
fprintf (stderr, "%s() error: file open failed '%s'.\n",
__func__, fn);
return -1;
}
ssize_t nchr = 0;
ssize_t idx = 0;
char *p = NULL;
/* position fd & read line */
if ((nchr = lseek (fd, *offset, SEEK_SET)) != -1)
nchr = read (fd, buf, sz);
close (fd);
if (nchr == -1) { /* read error */
fprintf (stderr, "%s() error: read failure in '%s'.\n",
__func__, fn);
return nchr;
}
/* end of file - no chars read
(not an error, but return -1 )*/
if (nchr == 0) return -1;
p = buf; /* check each chacr */
while (idx < nchr && *p != '\n') p++, idx++;
*p = 0;
if (idx == nchr) { /* newline not found */
*offset += nchr;
/* check file missing newline at end */
return nchr < (ssize_t)sz ? nchr : 0;
}
*offset += idx + 1;
return idx;
}示例输入
除了第二个文件在每一行文本之间包含一个空行外,以下数据文件是相同的。
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
$ cat dat/captnjack2.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.输出
$ ./bin/readfile dat/captnjack.txt
line[ 0] : This is a tale (14 chars)
line[ 1] : Of Captain Jack Sparrow (23 chars)
line[ 2] : A Pirate So Brave (17 chars)
line[ 3] : On the Seven Seas. (18 chars)
$ ./bin/readfile dat/captnjack2.txt
line[ 0] : This is a tale (14 chars)
line[ 1] : (0 chars)
line[ 2] : Of Captain Jack Sparrow (23 chars)
line[ 3] : (0 chars)
line[ 4] : A Pirate So Brave (17 chars)
line[ 5] : (0 chars)
line[ 6] : On the Seven Seas. (18 chars)发布于 2015-10-13 15:38:05
实际上,您正在实现您自己版本的fgets。避免在fgets中逐字符读取不可查找的流是通过与FILE*数据结构相关联的内部缓冲区启用的。
在内部,fgets使用一个函数来使用“原始”输入输出例程来填充该缓冲区。在此之后,fgets逐个字符地通过缓冲区来确定'\n'的位置(如果有的话)。最后,fgets将内容从内部缓冲区复制到用户提供的缓冲区中,如果有足够的空间,则为null终止结果。
为了重新创建这个逻辑,您需要使用指向缓冲区的指针和指示缓冲区内当前位置的指针定义自己的FILE-like struct。之后,您需要定义自己版本的fopen,该版本初始化缓冲区并将其返回给调用方。您还需要编写自己版本的fclose来释放缓冲区。一旦所有这些都就位,您就可以按照上面概述的逻辑来实现fgets。
发布于 2015-10-13 15:45:27
char *buffer = malloc(sizeof (char) * 1024);
read(fd, buffer, 1024);
printf("%s", buffer);以上代码中有几个错误。
首先,malloc不是syscall (也不是恐怖(3) .)。根据定义,sizeof(char)为1。如果您只想使用syscalls (在系统(2)中列出),您将需要使用mmap(2),并且您应该请求页面大小的多个虚拟内存(参见getpagesize(2)或sysconf(3).),这通常是(但并不总是)4千字节。如果您可以使用malloc,您应该针对它的失败进行编码,并且最好将获得的缓冲区归零,所以至少
const int bufsiz = 1024;
char*buffer = malloc(bufsiz);
if (!buffer) { perror("malloc"); exit(EXIT_FAILURE); };
memset(buffer, 0, bufsiz);然后,更重要的是,读(2)将返回一个您应该始终使用的数字(至少在失败时是这样):
ssize_t rdcnt = read(fd, buffer, bufsiz);
if (rdcnt<0) { perror("read"); exit(EXIT_FAILURE); };如果rdcnt为正,则通常会增加一些指针(以rdcnt字节表示)。零计数意味着文件的结尾。
最后,您的printf正在使用<stdio.h>,而您可能使用写(2)。如果使用printf,请记住它正在缓冲。要么用\n结束格式,要么使用弗卢什(3)
如果使用printf,请确保字符串以零字节结束。一种可能是将bufsiz-1传递给您的read;因为我们以前对区域进行了零化,所以肯定会有一个终止的零字节。
顺便说一句,您可以学习C标准库的一些C标准库实现的源代码,如musl-libc或GNU libc。
不要忘记使用所有警告和调试信息(gcc -Wall -Wextra -g)进行编译,以使用调试器(gdb),可能是缬磨 & 斯特拉斯(1)
https://stackoverflow.com/questions/33106505
复制相似问题