加载文本文件,优化

C++ Load text file, optimization

本文关键字:优化 文件 文本 加载      更新时间:2023-10-16

这是我的源代码加载文本文件,并将每行分隔为单个项目(单词)。

如何进一步优化代码?测试空行(和其他结构)(在我看来)有点低效....

typedef std::vector < std::string >  TLines;
typedef std::vector < std::vector < std::string > > TItems;
TItems TFloadFile ( const char * file_name )
{
    //Load projection from file
    unsigned int lines = 0;
    char buffer[BUFF];
    FILE * file;
    TItems file_words;
    TLines file_lines;

    file = fopen ( file_name, "r" );
    if ( file != NULL )
    {
            for ( ; fgets ( buffer, BUFF, file ); )
            {
                    //Remove empty lines
                    bool empty_line = true;
                    for ( unsigned i = 0; i < strlen ( buffer ); i++ )
                    {
                            if ( !isspace ( ( unsigned char ) buffer[i] ) )
                            {
                                    empty_line = false;
                                    break;
                            }
                    }
                    if ( !empty_line )
                    {
                            file_lines.push_back ( buffer );
                            lines++;
                    }
            }

            file_words.resize ( lines + 1 );
            for ( unsigned int i = 0; i < lines; i++ )
            {
                    char * word = strtok ( const_cast<char *> ( file_lines[i].c_str() ), " t,;rn" );
                    for ( int j = 0; word; j++, word = strtok ( 0, " t;rn" ) )
                    {
                            file_words[i].push_back ( word );
                    }
            }
            fclose ( file );
    }
    return file_words;
}

for ( unsigned i = 0; i < strlen ( buffer ); i++ )行效率非常低,因为您每次都要通过循环计算buffer的长度。然而,这可能会被编译器优化掉。

你在没有reserve()任何空间的情况下将物品推到std::vector s上。对于大型文件,这将涉及大量开销,因为为了调整它们的大小,需要复制向量的内容。我刚刚读了@Notinlist的回答,里面已经谈到了std::vector::resize()的低效率。

与其通过重复的fgets()调用将每行读取到一个向量中,不如简单地确定文件中的字节数,动态地分配一个char数组来保存它们,然后将字节转储到其中?然后,您可以解析单词并将它们存储在file_words中。这将比您当前使用的方法更有效。

在优化之前,你能解释一下文件有多大,代码当前执行需要多长时间,以及为什么你认为它还没有IO绑定(即由于硬盘速度)。你认为需要多长时间?对文件中数据类型的一些概念也会很好(例如平均行长度,空行的平均比例等)。

也就是说,将remove-empty-line循环与单词标记循环结合起来。然后,您可以完全删除TLines,并避免std::string结构和向量推退。我还没有检查这个代码的工作,但它应该足够接近给你的想法。它还包括一个更有效的空行定位器:

if ( file != NULL )
{
    for ( ; fgets ( buffer, BUFF, file ); )
    {
        bool is_empty = true;
        for (char *c = buffer; *c != ''; c++)
        {
            if (!isspace(c))
            {
                is_empty = false;
                break;
            }
        }
        if (is_empty)
            continue;
        file_words.resize ( lines + 1 );
        char * word = strtok ( buffer, " t,;rn" );
        for ( int j = 0; word; j++, word = strtok ( 0, " t;rn" ) )
        {
                file_words[i].push_back ( word );
        }
        lines++;
    }
    fclose ( file );
}

For one

file_lines.push_back ( buffer );

这是一个非常昂贵的行。如果不必使用vector,则可以使用list。可以在完成任务后将列表转换为向量。

如果你绝对需要使用vector来实现这个目的,那么你应该使用一些指数递增,比如:

if(file_lines.size()<=lines){
    file_lines.resize((int)(lines * 1.3 + 1));
}

这样,你将有更少的cpu密集型。resize()操作,以最小的内存消耗开销为代价。

简化并转换为std::list而不是std::vector

typedef std::list< std::list< std::string > > TItems;
TItems TFloadFile ( const char * file_name )
{
    using namespace std;
    //Load projection from file
    ifstream file(file_name);
    TItems file_words;
    string line;
    for(getline(file,line); !file.fail() && !file.eof(); getline(file,line))
    {
        file_words.push_back(list<string>());
        list<string> &words(file_words.back());
        char *word = strtok((char*)line.c_str(), " t,;rn" );
        for(; word; word=strtok( 0, " t;rn" ))
        {
            words.push_back( word );
        }
        if(!words.size())
            file_words.pop_back();
    }
    return file_words;
}