使用C++ STL库统计一散文中单词出现次数和行号

在开发过程中经常会遇到文件处理的情形,例如统计一篇文章单词的数量、行数、出现频率最高的几个单词等等。这篇文章主要通过C++来解析一篇文章,实现每个单词(不区分大小写)出现的总次数和出现的行号的统计。

1 演示程序

文件处理能比较好地考验对开发语言基础技能的掌握能力,因为这需要去考虑数据的读取、数据的存储方式、数据的处理等等,可能不同的处理方法会得到不同的效率和结果。

下面的代码主要是使用C++的STL库解析一篇英文散文(网上看到不错就wget下来了),涉及的编程基本点如下:

1、STL容器中的map和vector容器;
2、ifstream库文件流的操作;
3、string的分割查找find、获取子串substr、去除非法字符等待;
4、c++中的基本知识点:构造函数(包括常量的初始化)、引用、对象的构造和析构等等。

文章内容如上图所示,下面直接呈上代码:

/** 
 * @FileName    stl_apply_readfile_1.cpp
 * @Describe    A simple example for using c++ STL to calculate words and line numbers in an article.
 * @Author      vfhky 2017-04-16 16:44 https://typecodes.com/cseries/stlcalcarticlewordlines1.html
 * @Compile     g++ stl_apply_readfile_1.cpp -o stl_apply_readfile_1
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;


class CFileHandle
{
public:
    CFileHandle( const string &s_file_name ) : s_m_file_name(s_file_name)
    {
        i_m_line_number = 0;
    }

    //读取每行的数据,然后进行处理
    void ReadFile( const string &s_file_name )
    {
        ifstream inFile;

        //以二进制可读的方式打开文件,也可以使用: inFile.open( s_file_name.data() );
        inFile.open( s_file_name.c_str(), ios::in | ios::binary );

        //Checks if the file stream has an associated file.
        assert( inFile.is_open() );

        //每一行的数据
        string s_line_buf;

        //读取一行内容    getline( inFile, s_line_buf )
        while( getline( inFile, s_line_buf, inFile.widen('n') ) )
        {
            ++i_m_line_number;

            //统一转换成大写:也可以使用 transform( s_line_buf.begin(), s_line_buf.end(), s_line_buf.begin(), ::toupper );
            transform( s_line_buf.begin(), s_line_buf.end(), s_line_buf.begin(), (int (*)(int))toupper );

            //开始数据处理
            HandleLine( s_line_buf );
        }

        //关闭文件流
        inFile.close();
    }

    //对每行的数据进行解析
    void HandleLine( const string &s_line_buf )
    {
        string s_word_buf;
        int i_start=0, i_last=s_line_buf.size();
        //std::size_t i_split_pos;
        int i_split_pos = 0;

        while( i_start < i_last )
        {
            i_split_pos = s_line_buf.find( ' ', i_start );
            string s_word;

            //if( i_split_pos == std::string::npos )
            if( i_split_pos == -1 )
            {
                s_word = s_line_buf.substr( i_start, i_last );
                HandleUnkind( s_word );
                HandleWord( s_word );
                break;
            }
            /**
                避免空格,也可以使用下面这两行来去掉行首和行尾的空格
                s_line_buf.erase( 0, s_line_buf.find_first_not_of(" ") );
                s_line_buf.erase( s_line_buf.find_last_not_of(" ") + 1 );
             */
            else if( i_start != i_split_pos )
            {
                s_word = s_line_buf.substr( i_start, i_split_pos-i_start );
                HandleUnkind( s_word );
                HandleWord( s_word );
                i_start = i_split_pos + 1;
            }
            //对于行首的空格不进行处理
            else
            {
                i_start = i_split_pos + 1;
            }
        }
    }

    //去除每个单词可能包含的非字符(除0~9和A~Z外的数据)
    void HandleUnkind( string &s_word_buf )
    {
        assert( s_word_buf.size() );

        //char c_word_buf[s_word_buf.size()+1]; 在vs中会报错:error C2131: 表达式的计算结果不是常数
        char *c_word_buf = new char[s_word_buf.size()+1]();

        unsigned int j = 0;
        for( unsigned int i=0; i= 0x30 && s_word_buf[i] = 0x41 && s_word_buf[i] <= 0x5A ) )
            {
                c_word_buf[j++] = s_word_buf[i];
            }
        }

        s_word_buf = c_word_buf;
        delete c_word_buf; 
    }

    //对每个单词的处理
    void HandleWord( const string &s_word_buf )
    {
        map<string, vector >::iterator mapit = mapobj.find( s_word_buf );

        //如果该单词不存在
        if( mapit == mapobj.end() )
        {
            vector vect;
            vect.push_back( i_m_line_number );
            mapobj.insert( make_pair( s_word_buf, vect ) );
        }
        else
        {
            mapit->second.push_back( i_m_line_number );
        }
    }

    //遍历map对象
    void Traverse()
    {
        map< string, vector >::iterator mapit = mapobj.begin();
        cout << "WordsttttCountsttttLines" << endl;
        cout << "---------------------------------------------------------------------" << endl;
        for( ; mapit != mapobj.end(); ++mapit )
        {
            cout <first << "tttt" <second.size() << "tttt";
            for( vector::iterator vectorit = mapit->second.begin(); vectorit != mapit->second.end(); ++vectorit )
            {
                cout << *vectorit << " ";
            }
            cout << endl;
        }
    }

    //获取文件总行数
    const unsigned int GetTotalLines() const
    {
        return i_m_line_number;
    }

private:
    map<string, vector > mapobj;
    unsigned int i_m_line_number;
    const string s_m_file_name;
};


int main( int argc, char **argv )
{
    //文件所在路径
    const string s_file_name =  "STL_APPLY_READFILE_1";

    CFileHandle *pCFileHandle = new CFileHandle( s_file_name );

    //开始处理文件
    pCFileHandle->ReadFile( s_file_name );
    //打印总行数
    cout << "[" << __FILE__ << ":" << __LINE__ << "] Total Lines=[" <GetTotalLines() << "]." <Traverse();

    delete pCFileHandle;

    return 0;
}

2 使用g++编译器进行编译并执行

使用g++或者之前写的这个Makefile文件进行编译,结果如下图所示。

接着执行程序 stl_apply_readfile_1 ,效果如下图所示(图片大小的限制只显示了一部分结果)。另外,上面C++程序中的数据处理函数 HandleUnkind 相对比较 粗略 :只简单过滤了非数字和字母的字符。这样会出现类似把 YOU'R 这样的数据处理成 YOUR 的情况,大家可以进行代码改进做更精细化的处理。

TypeCodes稿源:TypeCodes (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合编程 » 使用C++ STL库统计一散文中单词出现次数和行号

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录