统计大量文本中重复字符串的最大个数

有大量中文繁体的文本,都是网上摘取的,大小有6G。需要提取文本中相同的字符串的最大个数

有几个关键问题:

1.字符串的粒度如何确定?如何表示字符串的唯一位置索引?

2.字符串过多,如何快速确定是否相同,时间复杂度要尽量低

3.文本过多,无法放入内存怎么办

4.如何统计相同hash值的个数,以及记录它们的索引位置

对于1,每次读取一行,然后以100个字节为一组作为比较的字符串。每行最后一组,以实际读取到的字符串大小为准;将行号和所在组号作为二元组,唯一标识字符串hash值位置索引

对于2,必须hash了。但是冲突如何解决?网上有很多字符串hash算法,冲突率比较低。本人用二次hash来尽量避免冲突,就算有个别误差,也可以接受

对于3,由于最终比较的是hash值,将它们再次hash到10个小文件中,这样保证同一个hash值在同一个文件中。然后分别统计每个小文件中最大的重复度,最后取最大值输出即可。

对于4,用STL中的map,将两个hash值作为map的key,而vector<索引>作为map的value值,即map<HashKey_S, vector<PositionOfText_S>, Classcomp_S>,对于key还要定义小于号操作符Classcomp_S

//map中key大小比较函数对象
struct Classcomp_S {
    bool operator() (const HashKey_S &lkey, const HashKey_S &rkey) const
    {
        bool flag = false;
        if (lkey.uifirsthash < rkey.uifirsthash)
            flag = true;
        else if(lkey.uifirsthash == rkey.uifirsthash)
        {
            if (lkey.uisecondhash < rkey.uisecondhash)
                flag = true;
        }
        return flag;
    }
};



写代码中发现,有些字符串重复度很高,它们是一些不可打印的相同字符或者几乎相同的字符。最后用函数countDiffChar统计不同字符个数,将比较小的过滤掉。

代码写的比较乱,"../../data/filelist.txt"是6G原始文本的文件名列表,"../../data/hash2line.txt"是提取hash值和位置索引后存储的文件,之后还要将它切分。

代码如下:

main.cpp

#include <iostream>
#include "statSameLine.h"
using namespace std;

int main()
{
    MakeResult();
    return 0;
}


statSameLine.h

#ifndef STATSAMELINE_H
#define STATSAMELINE_H

typedef unsigned char BYTE;
typedef unsigned int DWORD;
typedef unsigned short WORD;


struct HashKey_S
{
	HashKey_S(DWORD uiFirst = 0, DWORD uiSecond = 0):uifirsthash(uiFirst), uisecondhash(uiSecond){
	}
	HashKey_S(const HashKey_S &stHash):uifirsthash(stHash.uifirsthash), uisecondhash(stHash.uisecondhash){
	}
    DWORD uifirsthash;
    DWORD uisecondhash;
};

struct PositionOfText_S
{
    PositionOfText_S(DWORD uiFile = 0, DWORD uiLine = 0, DWORD uiBatch = 0):uiFileNum(uiFile), uiLineNum(uiLine), uiBatch(uiBatch){

    }

    PositionOfText_S(const PositionOfText_S &pos):uiFileNum(pos.uiFileNum), uiLineNum(pos.uiLineNum), uiBatch(pos.uiBatch)
    {

    }

	DWORD uiFileNum;
    DWORD uiLineNum;
    DWORD uiBatch;
};

struct Hash2LineTable_S
{
	DWORD uiFileNum;
    DWORD uiLineNum;
    DWORD uiBatch;
    HashKey_S sthashkey;
};


void TestStl();
void MakeResult();



#endif // STATSAMELINE_H


statSameLine.cpp

#include "statSameLine.h"

#include <cstring>
#include <cassert>
#include <cstdlib>
#include <cstdio>

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <iomanip>
#include <locale>
#include <tchar.h>

#include "hash.h"

using namespace std;
//map中key大小比较函数对象
struct Classcomp_S {
    bool operator() (const HashKey_S &lkey, const HashKey_S &rkey) const
    {
        bool flag = false;
        if (lkey.uifirsthash < rkey.uifirsthash)
            flag = true;
        else if(lkey.uifirsthash == rkey.uifirsthash)
        {
            if (lkey.uisecondhash < rkey.uisecondhash)
                flag = true;
        }
        return flag;
    }
};
//去除字符串头尾空格与tab
string trimEnd(string &str)
{
    const string delim =" \t\r" ;
    string r=str.erase(str.find_last_not_of(delim)+1);
    return r.erase(0,r.find_first_not_of(delim));
}
//统计字符串中不同字符的个数,目的是为了去掉那些大量无意义的相同字符
int countDiffChar(string &stext)
{
    const int isize = 256;
    int bitarr[isize] = {0};
    for (size_t i = 0; i < stext.size(); ++i)
    {
        int iindex = (int)stext[i];
        bitarr[iindex] = 1;
    }

    int icount = 0;
    for (int i = 0; i < isize; ++i)
    {
        if (bitarr[i] != 0)
            ++icount;
    }
    return icount;

}
//存储一行文本,将6G文件的hash值以及每段文本所在位置保存,便于第二次处理
void storeOneLine(ofstream &ofs, const string &sLine, DWORD uiFileNum, DWORD uiLineNum, int &iFilterNumber)
{
	
    const int iLengthOfBatch = 100;

    int iAllBatches, iLineSize;


    DWORD uiHashcode;

    char szBatchLine[iLengthOfBatch + 1];

    Hash2LineTable_S stHash2LineTable;

    iLineSize = sLine.size();
    iAllBatches = iLineSize / iLengthOfBatch + 1;

	int iHashNum = 0;
	

    for (int i = 0; i < iAllBatches; ++i)
    {
        int iBegin, iEnd, iLength = iLengthOfBatch;

        iBegin = i * iLengthOfBatch;
        iEnd = iBegin + iLengthOfBatch;
        //
        if (iBegin >= iLineSize)
            break;
        if (iEnd > iLineSize)
            iLength = iLineSize - iBegin;

        memset(&stHash2LineTable, 0, sizeof(Hash2LineTable_S));
        memset(szBatchLine, 0, sizeof(szBatchLine));

        //here is tangle, .................
        string sSubLine;

        sSubLine = sLine.substr(iBegin, iLength);
        sSubLine = trimEnd(sSubLine);
		size_t uiLenOfSubstr = sSubLine.size();
		//cout << "uilen is " << uiLenOfSubstr << endl;
        if (uiLenOfSubstr < 50)
        {
            continue;
        }
        if (countDiffChar(sSubLine) < 3)
        {
            ++iFilterNumber;
            continue;
        }

		sprintf(szBatchLine, "%s", sSubLine.c_str());

        uiHashcode = BKDRHash(szBatchLine);
        stHash2LineTable.sthashkey.uifirsthash = uiHashcode;
        uiHashcode = APHash(szBatchLine);
        stHash2LineTable.sthashkey.uisecondhash = uiHashcode;
		stHash2LineTable.uiFileNum = uiFileNum;
        stHash2LineTable.uiLineNum = uiLineNum;
		
        stHash2LineTable.uiBatch = i;
        //wirte one Hash2LineTable_S to file
        ofs.write((const char*)(&stHash2LineTable), sizeof(Hash2LineTable_S));
    }
}
//根据行号以及所在组,输出对应文本
void outputSpecifyFile(ofstream &ofs, const string &sfilename, DWORD uiLine, DWORD uiBatch)
{

    const int iLengthOfBatch = 100;

    ifstream ifs;
    ifs.open((sfilename.c_str()), ios::in);
    assert(ifs.is_open());
    string sLine, stHash2LineTable;

    DWORD uiLineNum = 0;
    while (getline(ifs, sLine))
    {
        ++uiLineNum;
        if (uiLineNum == uiLine)
            break;
        sLine.clear();
    }
    ifs.close();

    int iLineSize, iBegin, iEnd, iLength = iLengthOfBatch;
    iLineSize = sLine.size();

    iBegin = uiBatch * iLengthOfBatch;
    iEnd = iBegin + iLengthOfBatch;
    //
    if (iBegin >= iLineSize)
    {
        cout << "error" << endl;
        return;
    }

    if (iEnd > iLineSize)
        iLength = iLineSize - iBegin;


    stHash2LineTable = sLine.substr(iBegin, iLength);
    stHash2LineTable = trimEnd(stHash2LineTable);
    if (stHash2LineTable.size() < 50)
    {
        cout << "stHash2LineTable.size() < 50, error" << endl;
        return;
    }
    /*cout << "the size of substring is " << stHash2LineTable.size() << "-->";

    cout << "***************start\n";*/
	ofs.write((const char*)(stHash2LineTable.c_str()), stHash2LineTable.size());
	ofs << endl;
	
}

void parse_one_file(const char *pconefile, ofstream &ofs,
                   vector<DWORD> &vecline, DWORD uiFileNum, int &iFilterNumber)
{
    cout << "parse_one_file:" << pconefile << endl;
    int imaxbytes_line = 0;

    DWORD uiLineNum = 0;

    ifstream ifs;
    ifs.open((pconefile), ios::in);
    assert(ifs.is_open());
    //store every line of char
    string sLine;

	//ofstream ofsUtf8;
	//ofsUtf8.open("utf8_chinese.txt", ios::binary );

    while (getline(ifs, sLine))
    {
        ++uiLineNum;
        //erase some chars: \r, \t, \s
        sLine = trimEnd(sLine);
        int iLineSize = sLine.size();
        if (iLineSize == 0)
        {
            continue;
        }
        if (iLineSize > imaxbytes_line)
        {
            imaxbytes_line = iLineSize;
        }
        storeOneLine(ofs, sLine, uiFileNum, uiLineNum, iFilterNumber);
		/*ofsUtf8.write((char*)(sLine.c_str()), sLine.size());
		ofsUtf8 << endl;
		if (uiLineNum > 100)
		{
			
			break;
		}*/
        sLine.clear();
		

    }
	//ofsUtf8.close();
    ifs.close();

	//uiLineNum from 1 to end
    vecline.push_back(uiLineNum);
    cout << imaxbytes_line << " is max bytes of every line" << endl;
}
//解析原始文本,存储每个字符串对应的hash值以及索引位置
int parse_all_file(const char *pcallfile, const char *pcwritefile)
{
	vector<DWORD> vecline;
    char cread;
    ifstream ifs;
    ofstream ofs;

    int iFilterNumber = 0;

	ofs.open(pcwritefile, ios::out | ios::binary);
    assert(ofs.is_open());
    ifs.open(pcallfile, ios::in);
    assert(ifs.is_open());
    string sLine;
    DWORD uiFileNum = 0;
    while (ifs.read(&cread, 1))
    {
        if (cread != 0x0a)
        {
            sLine.push_back(cread);
            continue;
        }
		++uiFileNum;

        sLine = string("../../../data/alldata/") + sLine;
        parse_one_file(sLine.c_str(), ofs, vecline, uiFileNum, iFilterNumber);
        sLine.clear();
    }
    ofs.close();
    ifs.close();
    ofs.open("../../../data/linenum.txt");

    for (size_t i = 0; i < vecline.size(); ++i)
    {
        ofs << vecline[i] << endl;
		cout << vecline[i] << endl;
    }
    ofs.close();

    return iFilterNumber;

}
//切分文件,便于直接在内存中建立map
void splitfile(const char *pcfile)
{
    ifstream ifs;
    ifs.open(pcfile, ios::in | ios::binary);
    assert(ifs.is_open());

    Hash2LineTable_S stHash2LineTable;
    DWORD uifirsthashcode;

    char szcbuffer[150], swritename[80];
    int inum = 0;
	const int ifilenum = 10;

    ofstream szofs[ifilenum];
    for(int i = 0; i < ifilenum; ++i)
    {
        sprintf (swritename, "../../../data/hash_%d.txt", i);
        szofs[i].open(swritename, ios::out | ios::binary);

        memset(swritename, 0, sizeof(swritename));
    }

    while(ifs.read((char*)(&stHash2LineTable), sizeof(Hash2LineTable_S)))
    {
        uifirsthashcode = stHash2LineTable.sthashkey.uifirsthash;

        DWORD uihashvalue;
        sprintf(szcbuffer, "%d", uifirsthashcode);

        uihashvalue = ELFHash(szcbuffer) % ifilenum;

        if (uihashvalue == 1)
            ++inum;

        szofs[uihashvalue].write((char*)(&stHash2LineTable), sizeof(Hash2LineTable_S));

        memset(&stHash2LineTable, 0, sizeof(Hash2LineTable_S));
        memset(swritename, 0, sizeof(swritename));

    }
    ifs.close();
    cout << "splitfile hash value 1, num is " << inum << endl;

    for (int i = 0; i < ifilenum; ++i)
    {
        szofs[i].close();
    }
}



void getVecLine(vector<DWORD> &vecline)
{
    ifstream ifs;
    ifs.open("../../../data/linenum.txt", ios::out);
    assert(ifs.is_open());
    DWORD uiread;
    string sLine;
    vecline.clear();
    while (getline(ifs, sLine))
    {
        uiread = atoi(sLine.c_str());
        vecline.push_back(uiread);
    };
    ifs.close();
}


vector<PositionOfText_S> getSameLineOfCount(const char *pcfile, DWORD &uiDegreeOfDup, vector<PositionOfText_S> &stMaxPos)
{
    cout << "getSameLineOfCount, file name is " << pcfile << endl;
    vector<PositionOfText_S> *pmaxlineOfPos;
    ifstream ifs;
	ifs.open(pcfile, ios::in | ios::binary);
    assert(ifs.is_open());

    Hash2LineTable_S stHash2LineTable;
    memset(&stHash2LineTable, 0, sizeof(Hash2LineTable_S));


    map<HashKey_S, vector<PositionOfText_S>, Classcomp_S> maphash2postion;
    long lkeynum = 0;

    while (ifs.eof() == false)
    {
		//cout << ifs.tellg() << endl;
		ifs.read((char*)(&stHash2LineTable), sizeof(Hash2LineTable_S));
        ++lkeynum;
        DWORD uiFileNum, uiLine, uiBatch;
		uiFileNum = stHash2LineTable.uiFileNum;
        uiLine = stHash2LineTable.uiLineNum;
		if (uiLine > 498417)
		{
			cout << "uilinenum is large than 498417, its value is " << uiLine << endl;
			break;
		}
        uiBatch = stHash2LineTable.uiBatch;
        maphash2postion[stHash2LineTable.sthashkey].push_back(PositionOfText_S(uiFileNum, uiLine, uiBatch));
    }
    cout << "*************map size************\n";
//    cout << "the num of key value is " << lkeynum << endl;
    cout << "the num of map is " << maphash2postion.size() << endl;

    ifs.close();
    DWORD uimaxcount = 0;

    const HashKey_S *pmaxkey = NULL;
    vector<PositionOfText_S> *pvecposOfText = NULL;
    ofstream ofs;
    ofs.open("../../../data/hash2linetable.txt");
    /*保存每个字符串重复度,hashkey,以及位置(虽然本程序用不到)
     *
     *file format
     *1.uicount, the max count of same line, the size of vector<PositionOfText_S>
     *2.HashKey_S
     *3.many PositionOfText_S, the num is uicount
     */
    for (map<HashKey_S, vector<PositionOfText_S>, Classcomp_S >::iterator it = maphash2postion.begin(); it != maphash2postion.end(); ++it)
    {
        DWORD uicount;
        const HashKey_S *pkey = &(it->first);
        pvecposOfText = &(it->second);
        uicount = pvecposOfText->size();
        if (uicount > uimaxcount)
        {
            uimaxcount = uicount;
            pmaxlineOfPos = pvecposOfText;
            pmaxkey = pkey;
        }
		
        ofs.write((char*)(&uicount), sizeof(DWORD));
        ofs.write((char*)(pkey), sizeof(HashKey_S));
        for (size_t i = 0; i < uicount; ++i)
            ofs.write((char*)(&(pvecposOfText->at(i))), sizeof(PositionOfText_S));
    }
    ofs.close();
    cout << "maxcount is " << uimaxcount << endl;
//    cout << "maxline is " << uimaxline << endl;
  //  cout << "max key, first is " << pmaxkey->uifirsthash << ", second is " << pmaxkey->uisecondhash << endl;
    if (uimaxcount > uiDegreeOfDup)
    {
        uiDegreeOfDup = uimaxcount;
        stMaxPos = *pmaxlineOfPos;
    }

    return *pmaxlineOfPos;

}

void print_one_max_line(ofstream &ofs, const char *pcFileList, const PositionOfText_S &stPos, int *pibit)
{
    DWORD uiLine, uiFileNum;
	uiFileNum = stPos.uiFileNum;    
	uiLine = stPos.uiLineNum;

    ifstream ifs;
    ifs.open(pcFileList, ios::in);
    assert(ifs.is_open());
    string sLine;

    int inum = 0;
    while (getline(ifs, sLine))
	{
		++inum;
        if (inum == uiFileNum)
        {
            ofs << sLine << ":the num of line is " << uiLine << ", batch is " << stPos.uiBatch << endl;

            sLine = string("../../../data/alldata/") + sLine;
            outputSpecifyFile(ofs, sLine, uiLine, stPos.uiBatch);
            break;
        }
        sLine.clear();
    }
    ifs.close();
}

void output_result(const char *pcFileList, vector<PositionOfText_S> *pmaxlineOfPos, const char *pcName)
{
    cout << "output_result to file:" << pcName << endl;
    int isum = 0;
    ofstream ofs;
	ofs.open(pcName, ios::out | ios::binary);
    int szBit[30];
    for (vector<PositionOfText_S>::const_iterator cit = pmaxlineOfPos->begin(); cit != pmaxlineOfPos->end(); ++cit)
    {
		/*if (isum++ == 20)
		{
			break;
		}*/
        print_one_max_line(ofs, pcFileList, *cit, szBit);
    }
    ofs.close();
}

void MakeResult()
{
    const char *pcWriteName = "../../../data/hash2line.txt", *pcFileList = "../../../data/filelist.txt";
    const int ifilenum = 10;

    char szName[80];
    int iFilterNumber = 0;
	//解析所有文件
    //iFilterNumber = parse_all_file(pcFileList, pcWriteName);
	//将大文件划分为10个小文件
    //splitfile(pcWriteName);

    DWORD uiDegreeOfDup = 0;
    vector<PositionOfText_S> stPos, stMaxPos;
    for (int i = 0; i < ifilenum; ++i)
    {
        cout << "***every file, the pos of max line***\n";
        memset(szName, 0, sizeof(szName));
        sprintf(szName, "../../../data/hash_%d.txt", i);
		//获取每个小文件的最大重复子串
        stPos = getSameLineOfCount(szName, uiDegreeOfDup, stMaxPos);

        memset(szName, 0, sizeof(szName));
        sprintf(szName, "../../../data/hash_%d_maxpostext.txt", i);
        output_result(pcFileList, &stPos, szName);
        stPos.clear();
    }
	const char *pcOutFileName = "degree_of_dup_result.txt";
    //output_result(pcFileList, &stMaxPos, pcOutFileName);
	ofstream ofs;
	ofs.open(pcOutFileName, ios::out | ios::app);
	ofs << "**************" << endl;
    ofs << "uiDegreeOfDup is " << uiDegreeOfDup << endl;
    ofs << "filter num is " << iFilterNumber << endl;
	ofs.close();

}


hash.h

#ifndef HASH_H
#define HASH_H
typedef unsigned char BYTE;
typedef unsigned int DWORD;

DWORD SDBMHash(char *pcstr);

// RS Hash Function
DWORD RSHash(char *pcstr);
// JS Hash Function
DWORD JSHash(char *pcstr);

// P. J. Weinberger Hash Function
DWORD PJWHash(char *pcstr);

// ELF Hash Function
DWORD ELFHash(char *pstr);

// BKDR Hash Function
DWORD BKDRHash(char *pcstr);
// DJB Hash Function
DWORD DJBHash(char *pcstr);
// AP Hash Function
DWORD APHash(char *pcstr);
#endif // HASH_H

hash.cpp


#include "hash.h"

DWORD SDBMHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD hash = 0;

    while (*pcstr)
    {
        // equivalent to: hash = 65599*hash + (*pcstr++);
        hash = (*pcstr++) + (hash << 6) + (hash << 16) - hash;
    }

    return (hash & 0x7FFFFFFF);
}

// RS Hash Function
DWORD RSHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD b = 378551;
    DWORD a = 63689;
    DWORD hash = 0;

    while (*pcstr)
    {
        hash = hash * a + (*pcstr++);
        a *= b;
    }

    return (hash & 0x7FFFFFFF);
}

// JS Hash Function
DWORD JSHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD hash = 1315423911;

    while (*pcstr)
    {
        hash ^= ((hash << 5) + (*pcstr++) + (hash >> 2));
    }

    return (hash & 0x7FFFFFFF);
}

// P. J. Weinberger Hash Function
DWORD PJWHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD BitsInUnignedInt = (DWORD)(sizeof(DWORD) * 8);
    DWORD ThreeQuarters    = (DWORD)((BitsInUnignedInt  * 3) / 4);
    DWORD OneEighth        = (DWORD)(BitsInUnignedInt / 8);
    DWORD HighBits         = (DWORD)(0xFFFFFFFF) << (BitsInUnignedInt - OneEighth);
    DWORD hash             = 0;
    DWORD test             = 0;

    while (*pcstr)
    {
        hash = (hash << OneEighth) + (*pcstr++);
        if ((test = hash & HighBits) != 0)
        {
            hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
        }
    }

    return (hash & 0x7FFFFFFF);
}

// ELF Hash Function
DWORD ELFHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD hash = 0;
    DWORD x    = 0;

    while (*pcstr)
    {
        hash = (hash << 4) + (*pcstr++);
        if ((x = hash & 0xF0000000L) != 0)
        {
            hash ^= (x >> 24);
            hash &= ~x;
        }
    }

    return (hash & 0x7FFFFFFF);
}

// BKDR Hash Function
DWORD BKDRHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD seed = 131; // 31 131 1313 13131 131313 etc..
    DWORD hash = 0;

    while (*pcstr)
    {
        hash = hash * seed + (*pcstr++);
    }

    return (hash & 0x7FFFFFFF);
}

// DJB Hash Function
DWORD DJBHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD hash = 5381;

    while (*pcstr)
    {
        hash += (hash << 5) + (*pcstr++);
    }

    return (hash & 0x7FFFFFFF);
}

// AP Hash Function
DWORD APHash(char *pcstr)
{
    if (*pcstr == 0)
        return 0;
    DWORD hash = 0;
    int i;

    for (i=0; *pcstr; i++)
    {
        if ((i & 1) == 0)
        {
            hash ^= ((hash << 7) ^ (*pcstr++) ^ (hash >> 3));
        }
        else
        {
            hash ^= (~((hash << 11) ^ (*pcstr++) ^ (hash >> 5)));
        }
    }

    return (hash & 0x7FFFFFFF);
}




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值