我的C ++非成员getline()版本,它采用FILE *(由_wfopen()创建)而不是流,速度太慢

时间:2010-01-20 03:45:51

标签: c++

在C ++中,你可以在循环中使用非成员getline()和一个循环,如下所示:

#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;

int main() {
    ifstream in("file.txt");
    if (!in) {
        return EXIT_FAILURE;
    }
    for (string line; getline(in, line); ) {
        // Do stuff with each line
    }
}

但是,我想用_wfopen(“file.txt”,“r”)创建的FILE *代替,所以我创建了一个:

#include <cstdio>
#include <string>
#include <cstdlib>
#include <cwchar>
using namespace std;

bool getline(FILE* const in, string& s) {
    int c = fgetc(in);
    if (c == EOF) {
        return false;
    }
    s.clear();
    while (c != EOF && c != 10 && c != 13) {
        s += c;
        c = fgetc(in);
    }
    return true;
}

int main() {
    FILE* const in = _wfopen(L"file.txt", L"r");
    if (!in) {
        return EXIT_FAILURE;
    }
    for (string line; getline(in, line); ) {
        // Do stuff with the line
    }
    if (in) {
        fclose(in);
    }
}

它处理我想要的新行,并按照我想要的循环工作。它太慢了,因为我一次只读一个字符,一次在字符串中插入一个字符。例如,处理12MB文件需要6秒,而原始getline几乎立即执行。对于一个小文件而言,这并不是什么大不了的事,但对于一个2GB的文件,这就是一个问题。

我希望它能像C ++的getline()一样快,但我不认为如果不重新设计它就能让它更快。

那么,我应该如何重新设计它以使它更有效?

我知道我应该以块的形式进入缓冲区(例如矢量并在需要时调整大小),直到我找到()换行符或换行符并将该范围附加到字符串。但是,我并没有真正想象如何让它像我的char-by-char版本一样工作,特别是如果我读得太多并且必须将新行或换行符后的数据放回到流中以便它可以被消耗掉下一次迭代。

现在,VC ++有一个带有FILE *的wifstream,而STLPort也可能有一个。但是,我只使用Mingw 4.4.1。 (我不想使用STLPort,因为使用Mingw构建屁股很痛苦。)

我需要使用FILE *的原因是因为这是_wfopen()返回的内容。我需要使用_wfopen(),因为它支持wchar_t *路径,我将从Windows函数CommandLineToArgvW(CommandLineW()和&amp; argc)返回的wchar_t **数组中获取这些路径。 ifstream并没有走很远的路。

由于

2 个答案:

答案 0 :(得分:4)

如果您使用C ++编程,那么您应该使用C ++ I / O工具。说完了......

首先,您通过检查10和13来检查换行。您应该以文本模式打开文件,然后检查'\n'。此方法是可移植的,可以使用不同的行尾约定,也适用于非ASCII系统。

假设您 使用原生C FILE *,我会这样做:

#include <cstdio>
#include <cstring>
#include <string>

bool cgetline(FILE* const in, std::string &s)
{
    char buf[BUFSIZ+1] = {0};
    s.clear();
    while (fgets(buf, sizeof buf, in) != NULL) {
        char *end = strchr(buf, '\n');
        if (end == NULL) {
            /* We didn't see a newline at the end of the line,
               if we hit the end of file, then the last line wasn't terminated
               with a newline character.  Return it anyway. */
            if (feof(in)) {
                s.append(buf, strlen(buf));
                return true;
            } else {
                s.append(buf, sizeof buf - 1);
            }
        } else {
            s.append(buf, end - buf);
            return true;
        }
    }
    return false;
}

复杂的问题在于,当文件的最后一行没有以换行符结尾时,确保程序正确。

逐个字符地读取文件并附加到字符串可能是您的版本速度慢的原因。

答案 1 :(得分:0)

您的std::string实现可能不会以一种逐个附加多个字符的方式增长字符串。要尝试的一件事可能是使用std::string::reserve()在缓冲区已满时将字符串容量加倍。

编辑: 顺便说一句,我应该补充一点,如果您希望在文本模式下打开FILE*,则无需同时检查\n\r,因为适用于该平台的换行转换由C stdio函数在文本模式下自动执行。 (但是,如果您打算读取在其他平台上创建的文件(例如,在Unix上读取Windows文件),那么您需要检查各种类型的行结尾。)