在C语言中逐行读取文件,主要使用Syscalls

在C语言中逐行读取文件,主要使用Syscalls,c,C,我试图逐行读取和解析文件。 我只想使用简单的系统调用读取、打开、关闭。。。而不是fgets或getc,因为我希望在某种程度上学习基础知识。我看了一些类似问题的答案,但它们都使用FGET之类的 我现在拥有的是:我编写的一个函数将在一个文件的缓冲区中存储1024个字符 int main(void) { const char *filename = "file.txt"; int fd = open(filename, O_RDONLY); char *buffer = mal

我试图逐行读取和解析文件。 我只想使用简单的系统调用读取、打开、关闭。。。而不是fgets或getc,因为我希望在某种程度上学习基础知识。我看了一些类似问题的答案,但它们都使用FGET之类的

我现在拥有的是:我编写的一个函数将在一个文件的缓冲区中存储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继续读取我停止的文件

我不希望将整个文件存储在缓冲区中,然后对其进行解析。 我想在我的缓冲区中添加一行,然后解析该行并realloc我的缓冲区并继续读取文件

我曾想过这样的事情,但我觉得它优化得很糟糕,不确定以后在哪里添加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
   */
谢谢:

编辑:澄清的标题。

如果要使用“读取”一次读取一行FGET或getline的目的,则必须在找到每个“\n”后跟踪文件中的偏移量。然后,只需一次读取一行,在当前行之后的偏移量处开始下一次读取

我理解希望能够使用底层函数以及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;
}
输出


实际上,您正在实现自己版本的。通过与文件*数据结构关联的内部缓冲区,可以避免FGET中不可查找流的逐字符读取

在内部,fgets使用一个函数来使用原始输入输出例程填充该缓冲区。之后,fgets逐个字符地遍历缓冲区以确定'\n'的位置(如果有)。最后,fgets将内容从内部缓冲区复制到用户提供的缓冲区中,如果有足够的空间,null将终止结果

为了重新创建此逻辑,您需要定义自己的类似文件的结构,其中包含指向缓冲区的指针和指示缓冲区内当前位置的指针。之后,您需要定义自己的fopen版本,它初始化缓冲区并将其返回给调用者。您还需要编写自己版本的fclose来释放缓冲区。一旦所有这些都就绪,您就可以按照上面概述的逻辑实现FGET

char *buffer = malloc(sizeof (char) * 1024); 
read(fd, buffer, 1024);        
printf("%s", buffer);
上面的代码中有几个错误

首先,malloc不是系统调用,也不是。。。。。sizeofchar的定义是1。如果只想使用中列出的系统调用,则需要使用,并且应该请求页面大小的倍数的虚拟内存,请参阅或…,通常但不总是4 KB。 如果您可以使用malloc,那么您应该针对其失败进行编码,并且最好将获得的缓冲区归零,这样至少

const int bufsiz = 1024;
char*buffer = malloc(bufsiz);
if (!buffer) { perror("malloc"); exit(EXIT_FAILURE); };
memset(buffer, 0, bufsiz);
然后,更重要的是,返回一个至少在出现故障时应该始终使用的数字:

ssize_t rdcnt = read(fd, buffer, bufsiz);
if (rdcnt<0) { perror("read"); exit(EXIT_FAILURE); };
如果rdcnt为正,通常会将一些指针增加rdcnt字节。零计数表示文件结束

最后您的printf正在使用,您可能会使用 相反如果使用printf,请记住它是缓冲的。请使用结尾符结束格式,\n或使用

如果使用printf,请确保字符串以零字节结尾。一种可能是将bufsiz-1传递给您的read;由于我们之前已将区域归零,因此肯定会有一个终止的零字节

顺便说一句,您可以研究一些实现的源代码,例如或


不要忘记使用所有警告和调试信息gcc-Wall-Wextra-g进行编译,以使用调试器gdb,例如&

如何在“\n”处停止?为什么不呢?。。毕竟,这就是函数是专门为您试图实现的目的而编写的。您的代码是不安全的:read不为null终止缓冲区,因此您无法将其传递给printf。然后您必须实现自己版本的fgets,包括一些有趣的事情,比如缓冲、编写只需要系统调用的函数,以实现我自己的fgets和学习。这仅用于学习目的。无法正常工作。读取、打开、关闭不是标准的C函数。fgetc是一个标准函数。本质上,您并不是在学习如何使用C,而是如何实现C。那么不可查找的流呢?您基本上必须在一次读取时将整个文件缓冲在某个位置,并在一次读取后解析缓冲区。我认为在这种情况下,您不必缓冲整个文件。但是你肯定要缓冲其中的一部分。同意,你必须缓冲你能缓冲的任何东西,基本上通过文件填充/解析你有任何大小的缓冲区。对于上面的例子,如果它是不可查找的,您基本上需要移动它
您从main输出到readline,并按找到的“\n”打印每一行。在这种情况下,你只需要在一个函数中使用读写组合,我明白了。我将不得不重读几次,写下来并仔细考虑一下,但这明确地向我展示了正确的轨迹,系统调用的不同用途以及缓冲区的使用。谢天谢地,我并没有仅仅用系统调用来定义我的标题。感谢您提供有关缓冲区和读取的安全措施的附加信息。然后,您不允许使用malloc、仅使用mmap或使用某些静态或本地缓冲区。我可以使用syscalls的其他函数。Syscall只是对我的旧标题的引用。总之,毫无意义的评论。。。还是谢谢你的信息
const int bufsiz = 1024;
char*buffer = malloc(bufsiz);
if (!buffer) { perror("malloc"); exit(EXIT_FAILURE); };
memset(buffer, 0, bufsiz);
ssize_t rdcnt = read(fd, buffer, bufsiz);
if (rdcnt<0) { perror("read"); exit(EXIT_FAILURE); };