C将大文件读入char*数组太慢



我想读取一个大文件,而行的第一个字符不是" "。但是我写的代码非常慢。我怎样才能加快节奏呢?有没有比getline更好的解决方案?

void readString(const char *fn)
{
    FILE *fp;
    char *vString;
    struct stat fdstat;
    int stat_res;
    stat_res = stat(fn, &fdstat);
    fp = fopen(fn, "r+b");
    if (fp && !stat_res)
    {
      vString = (char *)calloc(fdstat.st_size + 1, sizeof(char));
      int dataEnd = 1;
      size_t len = 0;
      int emptyLine = 1;
      char **linePtr = malloc(sizeof(char*));
      *linePtr = NULL;
      while(dataEnd)
      {
        // Check every line
        getline(linePtr, &len, fp);
        // When data ends, the line begins with space (" ")
        if(*linePtr[0] == 0x20)
           emptyLine = 0;           
        // If line begins with space, stop writing
        if(emptyLine)
           strcat(vString, *linePtr);
        else
           dataEnd = 0;
      }
      strcat(vString, "");
      free(linePtr);
      linePtr = NULL;
    }
}
int main(int argc, char **argv){
    readString(argv[1]);
    return EXIT_SUCCESS;
}

我怎样才能加快程序?

程序性能方面最可疑的方面是strcat()。在每次调用时,它都需要从头扫描整个目标字符串,以找到附加源字符串的位置。因此,如果文件的行长度由一个常量(即使是一个大的常量)限定,那么您的方法的性能随文件长度的平方而变化。

渐近复杂性分析并不一定能说明整个问题。代码的I/O部分随文件长度线性扩展,并且由于I/O比内存中的数据操作要昂贵得多,因此对于足够小的文件,这将影响您的性能。如果你处于这种状态,那么你可能不会比现在做得更好。但是,在这种情况下,通过fread()立即读取整个文件,然后通过strstr():

扫描它以查找数据结束,这样做可能会更好一些。
size_t nread = fread(vString, 1, fdstat.st_size, fp);
// Handle nread != fdstat.st_size ...
// terminate the buffer as a string
vString[nread] = '';
// truncate the string after the end-of-data:
char *eod = strstr(vString, "n ");
if (eod) {
    // terminator found - truncate the string after the newline
    eod[1] = '';
} // else no terminator found

是线性扩展的,因此它也解决了渐近复杂性问题,但是如果感兴趣的数据通常比文件短得多,那么在这些情况下,它将使您执行比您需要做的更昂贵的I/O。在这种情况下,一种选择是像@laissez_faire建议的那样,分块阅读。另一种方法是调整您的原始算法以跟踪vString的结尾,以便使用strcpy()而不是strcat()来附加每个新行。该版本的关键部分看起来像这样:

char *linePtr = NULL;
size_t nread = 0;
size_t len = 0;
*vString = '';  // In case the first line is end-of-data
for (char *end = vString; ; end += nread) {
    // Check every line
    nread = getline(&linePtr, &len, fp);
    if (nread < 0) {
        // handle eof or error ...
    }
    // When data ends, the line begins with space (" ")
    if (*linePtr == ' ') {
        break;
    }
    strcpy(end, *linePtr);
}
free(linePtr);

另外,请注意

  • 您最初不需要零填充分配给*vString的内存,因为您只是要用真正感兴趣的数据覆盖那些零(然后忽略缓冲区的其余部分)。

  • 不应该强制转换malloc() -族函数的返回值,包括calloc()

您是否尝试过使用read读取文件,并在每个步骤中读取更大的数据块,然后在读取后解析数据?比如:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
char *readString(const char *fn)
{
    FILE *fp;
    char *vString;
    struct stat fdstat;
    int stat_res;
    stat_res = stat(fn, &fdstat);
    fp = fopen(fn, "r+b");
    if (fp && !stat_res) {
    vString = (char *) calloc(fdstat.st_size + 1, sizeof(char));
    int newline = 1;
    int index = 0;
    while (index < fdstat.st_size) {
        int len =
        fdstat.st_size - index >
        4096 ? 4096 : fdstat.st_size - index;
        char *buffer = (char *) malloc(len);
        int read_len = fread(buffer, 1, len, fp);
        int i;
        if (newline) {
        if (read_len > 0 && buffer[0] == ' ') {
            return vString;
        }
        newline = 0;
        }
        for (i = 0; i < read_len; ++i) {
        if (buffer[i] == 'n') {
            if (i + 1 < read_len && buffer[i + 1] == ' ') {
            memcpy(vString + index, buffer, i + 1);
            return vString;
            }
            newline = 1;
        }
        }
        memcpy(vString + index, buffer, read_len);
        index += read_len;
    }
    }
    return vString;
}
int main(int argc, char **argv)
{
    char *str = readString(argv[1]);
    printf("%s", str);
    free(str);
    return EXIT_SUCCESS;
}

相关内容

  • 没有找到相关文章

最新更新