C 在不知道行长度的情况下从文件中读取行

C 在不知道行长度的情况下从文件中读取行,c,file-io,C,File Io,我想一行一行地读入一个文件,以前不知道行的长度。以下是我目前得到的信息: int ch = getc(file); int length = 0; char buffer[4095]; while (ch != '\n' && ch != EOF) { ch = getc(file); buffer[length] = ch; length++; } printf("Line length: %d characters.", length); cha

我想一行一行地读入一个文件,以前不知道行的长度。以下是我目前得到的信息:

int ch = getc(file);
int length = 0;
char buffer[4095];

while (ch != '\n' && ch != EOF) {
    ch = getc(file);
    buffer[length] = ch;
    length++;
}

printf("Line length: %d characters.", length);

char newbuffer[length + 1];

for (int i = 0; i < length; i++)
    newbuffer[i] = buffer[i];

newbuffer[length] = '\0';    // newbuffer now contains the line.
int ch=getc(文件);
整数长度=0;
字符缓冲区[4095];
while(ch!='\n'&&ch!=EOF){
ch=getc(文件);
缓冲区[长度]=ch;
长度++;
}
printf(“行长度:%d个字符”,长度);
char newbuffer[长度+1];
for(int i=0;i
我现在可以计算出行长度,但只适用于短于4095个字符的行,加上两个字符数组似乎是执行任务的一种笨拙方式。 有没有更好的方法可以做到这一点(我已经使用了fgets(),但有人告诉我这不是最好的方法)


--你很接近了。基本上,您希望读取数据块并检查它们是否有
\n
字符。如果你找到了一个,很好,你就完了。如果你不这样做,你必须增加你的缓冲区(即分配一个新的缓冲区,其大小是第一个缓冲区的两倍,并将第一个缓冲区中的数据复制到新的缓冲区中,然后删除旧的缓冲区,并将新的缓冲区重命名为旧的缓冲区——或者如果你是C语言,则只需
realloc
),然后再阅读一些内容,直到找到结束

结束后,从缓冲区开始到
\n
字符的文本就是您的行。将其复制到缓冲区或就地处理,由您决定

准备好下一行后,可以在当前行上复制输入的“其余部分”(基本上是左移),并用输入的数据填充缓冲区的其余部分。然后再继续,直到数据用完


这当然可以进行优化,例如使用循环缓冲区,但对于任何合理的io绑定算法来说,这应该是足够的。

您可以从选择合适的大小开始,然后在需要更多空间时中途使用
realloc

int CUR_MAX = 4095;
char *buffer = (char*) malloc(sizeof(char) * CUR_MAX); // allocate buffer.
int length = 0;

while ( (ch != '\n') && (ch != EOF) ) {
    if(length ==CUR_MAX) { // time to expand ?
      CUR_MAX *= 2; // expand to double the current size of anything similar.
      buffer = realloc(buffer, CUR_MAX); // re allocate memory.
    }
    ch = getc(file); // read from stream.
    buffer[length] = ch; // stuff in buffer.
    length++;
}
.
.
free(buffer);

在调用
malloc
realloc
之后,您必须检查分配错误,您可能需要查看。如果您使用的是glibc系统,您可能有一个(非标准)函数可用。

这就是我为stdin所做的,如果您像
readLine(NULL,0)
那样调用它,该函数将为您分配一个大小为1024的缓冲区,并让它以1024的步长增长。如果使用
readLine(NULL,10)
调用函数,将得到一个步骤为10的缓冲区。如果你有一个缓冲区,你可以提供它的大小

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

char *readLine(char **line, size_t *length)
{
    assert(line != NULL);
    assert(length != NULL);

    size_t count = 0;

    *length = *length > 0 ? *length : 1024;

    if (!*line)
    {
        *line = calloc(*length, sizeof(**line));
        if (!*line)
        {
            return NULL;
        }
    }
    else
    {
        memset(*line, 0, *length);
    }

    for (int ch = getc(stdin); ch != '\n' && ch != EOF; ch = getc(stdin))
    {
        if (count == *length)
        {
            *length += 2;
            *line = realloc(*line, *length);
            if (!*line)
            {
                return NULL;
            }
        }

        (*line)[count] = (char)ch;

        ++count;
    }

    return *line;
}
#包括
#包括
#包括
#包括
字符*读线(字符**行,大小*长度)
{
断言(行!=NULL);
断言(长度!=NULL);
大小\u t计数=0;
*长度=*长度>0?*长度:1024;
如果(!*行)
{
*line=calloc(*长度,sizeof(**行));
如果(!*行)
{
返回NULL;
}
}
其他的
{
memset(*行,0,*长度);
}
对于(int ch=getc(stdin);ch!='\n'&&ch!=EOF;ch=getc(stdin))
{
如果(计数==*长度)
{
*长度+=2;
*行=realloc(*行,*长度);
如果(!*行)
{
返回NULL;
}
}
(*行)[计数]=(字符)ch;
++计数;
}
返回*行;
}

考虑scanf“%m”格式转换修饰符(POSIX)

引用scanf手册页:

可选的“m”字符。 这用于字符串转换 (%s,%c,%[),并释放 需要分配一个相应的缓冲区来保存输入:相反,scanf()分配一个足够的缓冲区 并将此缓冲区的地址分配给相应的指针参数,该参数应该是指向的指针 char*变量(调用前不需要初始化此变量)。调用方随后应在不再需要时释放(3)此缓冲区


值得注意的是,逐字符读取速度非常慢。您应该以大块(4-16k)的形式读取。@Blindy:标准库I/O有缓冲功能,所以这并不多比分块读取慢。将计数重置为0是否会导致缓冲区溢出?请一如既往。为什么在扩大内存大小后将计数重置回0?以前的内存是否仍然存在?很好!我相信我可以相信大多数类UNIX系统都安装了glibc,所以这绝对是一种很好的行读取方式。此外,
getline
已包含在最新的POSIX标准中,因此它现在是unix上的标准。但是,仍然不能保证它本身包含在c中。
char *arr = NULL ;
    // Read unlimited string, terminated with newline. Similar to dynamic size fgets.
if ( fscanf(stdin, "%m[^\n]", &arr) == 1 ) {
   // Do something with arr
   free(arr) ;
} ;