用C语言高效读取大型文本文件

rqqzpn5f  于 2023-02-15  发布在  其他
关注(0)|答案(2)|浏览(293)

我需要用C语言读取包含真实的或复杂数据的大型文本文件。目前,我使用的代码如下所示。这些函数很容易理解,对于小文件也能很好地工作。然而,我需要读取几GB大小的文件,与具有相同数据的二进制文件相比,这需要花费大量时间。(由于尺寸较大,项目在某个时候从文本文件切换到了二进制文件。我必须清理混乱。)

int io_read_array_real(char ordering, DTYPE *array,
                       int m, int n, FILE *ifile)
{
    int i, j, match;
    DTYPE elem;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            match = fscanf(ifile, "%e", &elem);
            if (match == 0)
            {
                printf("An error occurred while parsing the file!\n");
                return (-1);
            }
            if (ordering == 'C')
                *(array + RTC(i, j, m)) = elem;
            else if (ordering == 'R')
                *(array + i * n + j) = elem;
            else
                return (-1);
        }
    }
    return (0);
}

int io_read_array_complex(char ordering, CDTYPE *array,
                          int m, int n, FILE *ifile)
{
    int i, j, info;
    DTYPE zreal, zimag;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            info = fscanf(ifile, " (%e%ej) ", &zreal, &zimag);
            if (info != 2)
            {
                fprintf(stderr, "Input file in wrong format at (%d,%d) info = %d!\n"
                                "strerror: %s\n",
                        i, j, info, strerror(errno));
                return (-1);
            }
            if (ordering == 'C')
                *(array + RTC(i, j, m)) = zreal + I * zimag;
            else if (ordering == 'R')
                *(array + i * n + j) = zreal + I * zimag;
            else
                return (-1);
        }
    }
    return (0);
}

现在我想知道是否有更快的方法来阅读这些文件。它们肯定是这样的形式:

real:
1.233e-3 2.231e-1 ...
2.335e-4 8.241e-2 ...
.
.

complex:
(1.233e-3+3.239e-4j) (1.233e-3+3.239e-4j) ...
(7.684e-2+8.269e-5j) (1.233e-3+3.239e-4j) ...
.
.
wpcxdonn

wpcxdonn1#

你可以对整个文件进行内存Map,这样文件内容就可以直接在内存中寻址,操作系统的虚拟内存管理可以为你处理内存和分页,而不管文件的大小。
然后,您可以直接操作文件内容,就好像它是内存一样--没有显式的分配、重新分配。
用于内存Map文件的Windows和POSIX API不同,但是无论您使用什么系统,您都会发现大量的示例。
这样做的好处是操作系统可以在后台管理将数据加载到虚拟地址空间的操作,性能很可能取决于可用的物理内存量。此外,操作系统可以一次加载并分页文件的大块,这比一次阅读23字节的文件流要高效得多。
如果您坚持使用流I/O,那么至少可以通过阅读更大的块(如1024或4096字节)来减少文件I/O开销。

n6lpvg4x

n6lpvg4x2#

可能有改进:

  • 将重复的if (ordering == 'C')移出循环,并让它选择两个相似块中的一个进行迭代。
  • 将大部分目标地址计算移出最内层循环,以便fscanf()可以直接保存到目标位置。

但是对于显著的改进,OP是有限的,除非代码可以假设表示浮点值的文本格式并替换fscanf()。@Eric Postpischil
示例:
如果复数数据为(#.###e@#+#.###e@#j)#数字,@符号),则可能更快地编写代码。
下面是说明性的。确切的细节取决于OP数据格式的精确定义。OP应该描述各种方法。

// fscanf(ifile, " (%e%ej) ", &zreal, &zimag);
// (#.###e@#@#.###e@#j)
//  123456789 123456789 1
char buf[21+1+1]; // Input length + \n + \0
buf[19] = 0;
if (fgets(buf, sizeof buf, ifile)) {
  if (buf[19]!=')') Error_out; // trivial error check
  static const double expos[19] = { 1.0e-12, 1.0e-11, ..., 1.0e+6}; // Offset by 3: #.###
  int e = buf[7]=='-' ?  buf[8]-'0' : -(buf[8]-'0');
  *dest++ = (buf[1]*1000 + buf[3]*100 + buf[4]*10 + buf[5] - 1111*'0') * expos[e+9];
  ...
  • "多线程?"马丁·詹姆斯是一个值得测试的想法。

相关问题