在 Unix 上用 C 语言读取一行文本文件——我的read_line坏了



我想创建一个函数,从给定的文本文件中读取您选择的行。继续作为参数的函数(int fd打开和int line_number)它必须使用语言C和Unix系统调用(read和/或open)来做到这一点。它还应该读取任何空格,并且它不能有真正的限制(即行必须能够具有您选择的长度)。我做的函数是这样的:

char* read_line(int file, int numero_riga){
    char myb[1];
    if (numero_riga < 1) {
        return NULL;
    }
    char* myb2 = malloc(sizeof(char)*100);
    memset(myb2, 0, sizeof(char));
    ssize_t n;
    int i = 1;
    while (i < numero_riga) {
        if((n = read(file, myb, 1)) == -1){
            perror("read fail");
            exit(EXIT_FAILURE);
        }
        if (strncmp(myb, "n", 1) == 0) {
            i++;
        }else if (n == 0){
            return NULL;
        }
    }
    numero_riga++;
    int j = 0;
    while (i < numero_riga) {
        ssize_t n = read(file, myb, 1);
        if (strncmp(myb, "n", 1) == 0) {
            i++;
        }else if (n == 0){
            return myb2;
        }else{
            myb2[j] = myb[0];
            j++;
        }
    }
    return myb2;
}

直到最近,我还认为这会起作用,但它确实存在一些问题。使用消息队列,read_line读取的字符串将作为空字符串 ("\0") 接收。我知道消息队列不是问题,因为尝试传递普通字符串并没有产生问题。如果可能的话,我想要一个修复程序,并解释为什么我应该以某种方式纠正它。这是因为如果我不理解我的错误,我就有可能在未来重蹈覆辙。

编辑 1.根据答案,我决定添加一些问题。如何结束 myb2?有人可以根据我的代码给我一个示例吗?我如何提前知道构成一行 txt 要读取的字符数?

编辑 2.我不知道该行有多少字符,所以我不知道要分配多少字符;这就是我使用*100.

部分分析

您在以下位置出现内存泄漏:

char* myb2 = (char*) malloc((sizeof(char*))*100);
memset(myb2, 0, sizeof(char));
if (numero_riga < 1) {
    return NULL;
}

在分配内存之前检查numero_riga

以下循环充其量也是可疑的:

int i = 1;
while (i < numero_riga) {
    ssize_t n = read(file, myb, 1);
    if (strncmp(myb, "n", 1) == 0) {
        i++;
    }else if (n == 0){
        return NULL;
    }
}

您不会检查read()是否真的足够快地返回了任何内容,并且当您进行检查时,您会泄漏内存(再次)并忽略事先读取的任何内容,并且不会检测到错误(n < 0)。 当您检测到换行符时,您只需将 1 添加到 i 中即可。 任何时候都不会将读取的字符保存在缓冲区中(例如 myb2 )。 总而言之,这似乎已经彻底崩溃了...除非...除非您尝试从头开始读取文件中的第 N 行,而不是文件中的下一行,这更常见。

您需要做的是:

  • 扫描N-1线,注意EOF
  • 而另一个字节可用
    • 如果是换行符,则终止字符串并返回
    • 否则,请将其添加到缓冲区,如果没有空间,则分配空间。

实现

我想我可能会使用这样的函数get_ch()

static inline int get_ch(int fd)
{
    char c;
    if (read(fd, &c, 1) == 1)
        return (unsigned char)c;
    return EOF;
}

然后在主char *read_nth_line(int fd, int line_no)函数中,您可以执行以下操作:

char *read_nth_line(int fd, int line_no)
{
    if (line_no <= 0)
        return NULL;
    /* Skip preceding lines */
    for (int i = 1; i < line_no; i++)
    {
        int c;
        while ((c = get_ch(fd)) != 'n')
        {
            if (c == EOF)
                return NULL;
        }
    }
    /* Capture next line */
    size_t max_len = 8;
    size_t act_len = 0;
    char  *buffer  = malloc(8);
    int c;
    while ((c = get_ch(fd)) != EOF && c != 'n')
    {
        if (act_len + 2 >= max_len)
        {
            size_t new_len = max_len * 2;
            char *new_buf = realloc(buffer, new_len);
            if (new_buf == 0)
            {
                free(buffer);
                return NULL;
            }
            buffer = new_buf;
            max_len = new_len;
        }
        buffer[act_len++] = c;
    }
    if (c == 'n')
        buffer[act_len++] = c;
    buffer[act_len] = '';
    return buffer;
}

添加的测试代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern char *read_nth_line(int fd, int line_no);
…code from main answer…
int main(void)
{
    char *line;
    while ((line = read_nth_line(0, 3)) != NULL)
    {
        printf("[[%s]]n", line);
        free(line);
    }
    return 0;
}

这将从标准输入中读取每三行。 它似乎工作正常。 最好对边界条件(短线等)进行更详尽的检查,以确保它不会滥用内存。 (测试长度为 1 的行 — 仅限换行符 — 最多 18 个字符,valgrind表明它没问题。 随机较长的测试似乎也是正确的。

相关内容

  • 没有找到相关文章

最新更新