【问题标题】:Reversing every line in file using read() system call使用 read() 系统调用反转文件中的每一行
【发布时间】:2019-11-05 00:59:23
【问题描述】:

我试图让 read() 系统调用逐行读取我的文件并将每一行反转到标准输出。我的问题是让 read() 逐行读取我的文件,因为通常它只是读取整个文件。我希望 LINE_BUFFER 大小是一条线的最大大小。

我尝试实现一个函数来尝试这样做,但它似乎破坏了程序,我对如何解决这个问题有点迷茫。

这是我的代码:

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

#define LINE_BUFFER 1024

int charCount(const char *name1);
ssize_t readline (char *buf, size_t a, int b, off_t *offset);

int main(int argc, char* argv[]) {

    if(argc ==2){
        charCount(argv[1]);
    }else{
        printf("Provide a file\n");
    }
    return 0;
}

int charCount(const char *name1)
{
    char buffer[LINE_BUFFER];
    int fd;
    ssize_t len = 0;
    int nread;
    int i = 0;
    off_t offset = 0;
    if ((fd = open(name1, O_RDONLY)) == -1)
    {
        perror("Error in opening file");
        return (-1);
    }

    int size = lseek(fd,-1, SEEK_END);

    while(size>=0)
    {
        while((len = readline(buffer,LINE_BUFFER,fd,&offset)) != -1){
            write(1,buffer,1);
            lseek(fd, -2,SEEK_CUR);
            size--;
        }
    }
    close(fd);
    return(0);
}

ssize_t readline(char *buf, size_t a, int b, off_t *offset)
{
    int fd;

    ssize_t nchr =0;
    ssize_t idx =0;
    char *p = NULL;

    if ((nchr = lseek(fd, *offset, SEEK_SET)) != -1){
        nchr = read(fd,buf,a);
    }

    p = buf;
    while(idx<nchr && *p != '\n') p++,idx++;
    *p =0;

    if(idx == nchr) {
        *offset + nchr;
        return nchr < (ssize_t)a ? nchr : 0;
    }

    *offset += idx+1;
    return idx;
}

【问题讨论】:

  • Read 不知道“行”是什么。 read() 读取字节。您可以将整个文件读入缓冲区,然后逐行遍历生成的缓冲区,但除此之外,您必须一次读取 1 个字节并在执行过程中检查每个字节。
  • @DavidCRankin 所以你的意思是这不可能?
  • 是的,有可能,您可以一次一个字节地完成(这只是处理行的一种尴尬方式)。使用fgets() 一次将一行读入缓冲区要好得多。 (或将整个文件读入缓冲区,然后一次对整个事物进行操作)
  • fdreadline() 中设置在哪里?它需要是一个函数参数。
  • 哦,您将fd 传递给b 参数。为什么那个参数不叫fd

标签: c linux file


【解决方案1】:

当您说“通常它只是读取整个文件”时,我不确定您的意思。 read 的最大读取大小相当低,通常为 4KiB 或 8KiB。无论如何,我整理了一些代码来反转文件的行。

#include <assert.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int xopen(const char *, int);
void * Realloc(void *, size_t);
void reverse(char *, char *);
char * findchr(char *, char *, char);

int
main(int argc, char **argv)
{
        ssize_t rc;

        size_t siz = BUFSIZ;         /* size available to read into */
        char *buf = Realloc(NULL, BUFSIZ + siz); /* Pad the front */
        char *s = buf + BUFSIZ;      /* first char of a line */
        char *prev = s;              /* start of data from previous read */
        char *end = s;               /* one past last char read from input */
        int fd = argc > 1 ? xopen(argv[1], O_RDONLY) : STDIN_FILENO;

        while(( rc = read( fd, s, BUFSIZ )) > 0 ) {
                char *eol; /* A newline, or one past valid data */
                end = s + rc;

                if( (eol = findchr(s, end, '\n')) == end ) {
                        /* No newlines found in the last read.  Read more. */
                        if( end > buf + siz ) {
                                ptrdiff_t e_off = end - buf;
                                ptrdiff_t p_off = prev - buf;
                                siz += BUFSIZ;
                                buf = Realloc(buf, BUFSIZ + siz);
                                eol = end = buf + e_off;
                                prev = buf + p_off;
                        }
                        s = end;
                        assert( s <= buf + siz );
                        continue;
                }
                s = prev;
                do {
                        assert(*eol == '\n');
                        assert(eol < end);
                        reverse(s, eol-1);
                        s = eol + 1;
                        assert(s <= end);
                } while( (eol = findchr(s, end, '\n')) < end );
                assert(eol == end);
                assert(eol[-1] != '\n' || s == end);

                fwrite(prev, 1, s - prev, stdout);
                prev = buf + BUFSIZ - (end - s);
                memcpy(prev, s, end - s);
                eol = s = buf + BUFSIZ;
        }
        if(rc == -1) {
                perror(argc > 1 ? argv[1] : "stdin");
                return EXIT_FAILURE;
        }
        if(prev < s) {
                reverse(prev, s-1);
                fwrite(prev, 1, s - prev, stdout);
        }

        return EXIT_SUCCESS;
}

/*
 * Find v between str and end.  If not found,
 * return end.  (This is basically strchr, but
 * doesn't care about nul.)
 */
char *
findchr(char *str, char *end, char v) {
        assert(str <= end);
        while( str < end && *str != v )
                str += 1;
        return str;
}

void
reverse(char *start, char *end)
{
        for( ; start < end; start++, end-- ) {
                char tmp = *end;
                *end = *start;
                *start = tmp;
        }
}


void *
Realloc( void *buf, size_t s )
{
        buf = realloc( buf, s );
        if( buf == NULL) { perror("realloc"); exit(EXIT_FAILURE); }
        return buf;
}

int
xopen(const char *path, int flag)
{
        int fd = open(path, flag);
        if( fd == -1 ) { perror(path); exit(EXIT_FAILURE); }
        return fd;
}

【讨论】:

  • read 读取的最大大小相当低,通常为 4KiB 或 8KiB Linux is a bit under 2 GB 上的最大 read 大小:“在 Linux 上,@987654326 @(和类似的系统调用)将最多传输 0x7ffff000 (2,147,479,552) 个字节,返回实际传输的字节数。(这在 32 位和 64 位系统上都是如此。)“在 Solaris 上,我已经完成了 8 + GB read()/write() 操作没有问题。
  • @AndrewHenle.Wow...我想我已经过时了!但是要获得 8+GB,我怀疑您正在使用大页面或调整了其他一些参数。当尝试在我的个人笔记本电脑 (macos) 上读取大缓冲区时,我的范围为 512000 来自常规文件,而管道上为 30k-50k。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
相关资源
最近更新 更多