此文章是vip文章,如何查看?  

1,点击链接获取密钥 http://nicethemes.cn/product/view29882.html

2,在下方输入文章查看密钥即可立即查看当前vip文章


VC写的手机qq聊天记录导出工具

  • 时间:
  • 浏览:
  • 来源:互联网
源文件下载地址:http://download.csdn.net/source/2248555
最近把手机QQ从200版换到了2009版,于是以前的聊天记录也就看不到了。有时候想起和好友们开开心心聊天的点点滴滴,于是想把
手机QQ里面的聊天记录导出到文本文件中方便以后没事儿时看看。参考了文章:
http://blog.csdn.net/wenwu500/archive/2009/10/14/4668534.aspx
里的介绍,自己用VC++ 写了一个手机QQ2008和2009聊天记录提取器,该篇文章中对已聊天记录文件的存储格式介绍的不全面,这里
再补充一下。

首先是聊天记录的存放位置:
2008版:X:/QQ/你的QQ号码/msg/好友的QQ号码.db 文件中
2009版:X:/System/data/Tencent/QQ/自己的QQ号码/好友的QQ号码/msg.info 文件中

接下来分析聊天记录的格式,首先是2008版,用UltraEdit打开聊天记录db文件(打开方式是ASCII Escaped Unicode),右键选择十六进制
编辑回到字符显示状态,你就可以看到聊天记录了,不过每一条记录之间存在乱码,接下来分析去除这些乱码(接下来都是以16进制来说明):

手机QQ2008版聊天记录中,每一条记录都是以25 08开头,之后两个字节表示这条消息的长度:len=第三个字节×162+第二个字节-0x4e,

接着四个字节是这条消息发送者的QQ号,例如你的QQ号是453244328,如果这条消息是你发送的,则这四个字节的值就是a8 f5 03 1b(
用Windows自带的计算器可以计算出来,不过这里说明一下,文件中字节的存放顺序和变量中时反着的,例如,一个long型变量的值是
453244328,则用十六进制表示它应该是1b03f5a8(四个字节),而在文件中存放的顺序是a8 f5 03 1b。因此如果你一个字节一个字节的
读读到的顺序是反的);

之后两个字节的值是05 00,没什么特殊含义。
接下来的34个字节是这条消息发送或者接收到的时间,是一个UNICODE编码格式的字符串;
然后是两个无意义字节 00 00;
接着32个字节是这条消息发送者的昵称,也是UNICODE编码格式的字符串
最后是len个字节的消息内容,同样还是UNICODE格式的字符串,不过有时在消息中也包含有表情,如果消息字符串中出现14 xx,则这两个
字节就表示一个表情,其中14是前缀,xx是表情序号,对应着手机QQ中的一个表情

根据以上的格式就能读取聊天记录了,读取出来后将UNICODE格式的字符串转换成ANSI格式然后保存到文本文档里就成了:
void Export2008(FILE* fr, FILE* fw)
{
    fseek(fr,-1,SEEK_CUR);

    BYTE mark[15],null[4];
    char timestr[36],str[40],msg[2048];
    wchar_t wtimestr[18],wstr[20],buff[1024];
    memset(wtimestr,0x0000,36);
    memset(wstr,0x0000,40);
    memset(buff,0x0000,2048);
    long int len;
    fread(mark,sizeof(BYTE),10,fr);
    fread(wtimestr,sizeof(wchar_t),17,fr);
    fread(null,sizeof(BYTE),2,fr);
    fread(wstr,sizeof(wchar_t),16,fr);
    len=(((long int)mark[3])*16*16+(long int)mark[2])-0x4e;
    fread(buff,sizeof(char),len,fr);


    CString cstr(buff);
    wchar_t  enterc[2]={0};
    enterc[0]=0x0c;
    cstr.Replace(enterc,L"/r/n");
    wchar_t  face[8]={0};
    int clen=cstr.GetLength();
    for(int i=0;i<clen;i++)
    {
        if(cstr.GetAt(i)==0x14&&i+1)
        {
            short int nIDFace=(short int)cstr.GetAt(i+1);
            memset(face,0x00,14);
            if(GetUserFace(nIDFace,face))
            {
                wchar_t wface[3]={0};
                wface[0]=cstr.GetAt(i);
                wface[1]=cstr.GetAt(i+1);
                cstr.Replace(wface,face);
                clen=cstr.GetLength();
            }
        }
    }
    memset(buff,0x00,2048);
    wcsncpy(buff,cstr.GetBuffer(),cstr.GetLength());
    cstr.ReleaseBuffer();

    char c;
    fread(&c,sizeof(char),1,fr);
    memset(msg,0,2048);
    WCharToAChar(buff,msg,2048);
    
    memset(timestr,0,36);
    WCharToAChar(wtimestr,timestr,36);

    memset(str,0,40);
    WCharToAChar(wstr,str,40);

    fwrite(timestr,sizeof(BYTE),strlen(timestr),fw);
    fwrite("    ",sizeof(BYTE),strlen("    "),fw);
    fwrite(str,sizeof(BYTE),strlen(str),fw);
    fwrite("/r/n",sizeof(BYTE),strlen("/r/n"),fw);
    fwrite(msg,sizeof(BYTE),strlen(msg),fw);
    fwrite("/r/n/r/n",sizeof(BYTE),strlen("/r/n/r/n"),fw);
}


手机QQ2009版的聊天记录中,每一条记录都是以a8 xx(或者 A9 XX) 开头,然后以a8 xx(或者 A9 XX)结尾,例如某条消息如下:
A8 3A 4B 13 ED 06 00 00 00 00 00 00 80 D8 8F A1 
59 2F 66 60 4F 84 76 C4 9E D1 91 F6 65 3B 52 27 
54 26 20 14 00 4F 00 A8 3A 
它就是以A8 3A 开头,又以A8 3A结尾;但是A8 3A这两个字节并不是随机的,实际上它们代表的是这一条消息的长度:
len=第二个字节 +(第一个字节-0xa8)×162,注意:这个len不包括末尾的A8 XX 。

之后的四个字节是c++里面的time_t型变量值,有关这个time_t数据类型资料在百度上可以查到,这里就不多说了。这个值表示的是这条消息发送或者
接收的时间;

接下来六个字节是无用字节,值永远是00 00 00 00 00 00;

然后得一个字节是发送/接收标志,如果该字节是00,则代表是接收到的消息;如果是80,则是发送出去的消息;
之后的len字节便是消息的内容,同样也是UNICODE格式;

最后是和开头对应的两个字节A8 XX (或者 A9 XX);

同样根据分析出来的格式便可以读取出消息的内容,然后保存到文本文件中:
void Export2009(FILE* fr, FILE* fw)
{
    fseek(fr,-1,SEEK_CUR);
    BYTE markstr[2],flag;
    char timestr[4],nullstr[6],msg[2048];

    wchar_t buff[1024];
    memset(buff,0x0000,2048);
    long int  len,time;

    fread(markstr,sizeof(BYTE),2,fr);
    fread(timestr,sizeof(char),4,fr);

    char c;
    c=timestr[0];
    timestr[0]=timestr[3];
    timestr[3]=c;
    c=timestr[1];
    timestr[1]=timestr[2];
    timestr[2]=c;
    memcpy(&time,timestr,4);

    fread(nullstr,sizeof(char),6,fr);
    fread(&flag,sizeof(BYTE),1,fr);

    len=(long int)markstr[1]+((long int)markstr[0]-0xa8)*16*16;
    fread(buff,sizeof(BYTE),len,fr);


    CString cstr(buff);
    wchar_t  enterc[2]={0};
    enterc[0]=0x0c;
    cstr.Replace(enterc,L"/r/n");
    wchar_t  face[8]={0};
    int clen=cstr.GetLength();
    for(int i=0;i<clen;i++)
    {
        if(cstr.GetAt(i)==0x14&&i+1)
        {
            short int nIDFace=(short int)cstr.GetAt(i+1);
            memset(face,0x00,14);
            if(GetUserFace(nIDFace,face))
            {
                wchar_t wface[3]={0};
                wface[0]=cstr.GetAt(i);
                wface[1]=cstr.GetAt(i+1);
                cstr.Replace(wface,face);
                clen=cstr.GetLength();
            }
        }
    }
    memset(buff,0x00,2048);
    wcsncpy(buff,cstr.GetBuffer(),cstr.GetLength());
    cstr.ReleaseBuffer();
    
    fread(markstr,sizeof(BYTE),2,fr);   
    fread(&c,sizeof(char),1,fr);

    memset(msg,0,2048);
    WCharToAChar(buff,msg,2048);
    time_t  t=time;
    struct tm *ptrtime=gmtime(&t);
    char  strtime[26];
    sprintf(strtime,"%d-%d-%d  %d:%d:%d",ptrtime->tm_year+1900,ptrtime->tm_mon+1,ptrtime->tm_mday,ptrtime->tm_hour+8,ptrtime->tm_min,ptrtime->tm_sec);


    fwrite(strtime,sizeof(char),strlen(strtime),fw);
    fwrite("    ",sizeof(char),strlen("    "),fw);
    if(flag==0x80)
        fwrite(m_MyName,sizeof(char),strlen(m_MyName),fw);
    if(flag==0x00)
        fwrite(m_OpName,sizeof(char),strlen(m_OpName),fw);
    fwrite("/r/n",sizeof(char),strlen("/r/n"),fw);
    fwrite(msg,sizeof(BYTE),strlen(msg),fw);
    fwrite("/r/n/r/n",sizeof(char),strlen("/r/n/r/n"),fw);
}


下面是代表QQ表情值的对照表:
BOOL GetUserFace(short int nIDFace,wchar_t* face)
{
    if(0x4e==nIDFace) 
        wcsncpy_s(face,8,L"[#呲牙#]",8);
    else if(0x4d==nIDFace)
        wcsncpy_s(face,8,L"[#调皮#]",8);
    else if(0x79==nIDFace)
        wcsncpy_s(face,8,L"[#流汗#]",8);
    else if(0x8a==nIDFace)
        wcsncpy_s(face,8,L"[#偷笑#]",8);
    else if(0x99==nIDFace)
        wcsncpy_s(face,8,L"[#再见#]",8);
    else if(0x98==nIDFace)
        wcsncpy_s(face,8,L"[#敲打#]",8);
    else if(0xa2==nIDFace)
        wcsncpy_s(face,8,L"[#擦汗#]",8);
    else if(0x7c==nIDFace)
        wcsncpy_s(face,8,L"[#猪头#]",8);
    else if(0x62==nIDFace)
        wcsncpy_s(face,8,L"[#玫瑰#]",8);
    else if(0xb1==nIDFace)
        wcsncpy_s(face,8,L"[#菜刀#]",8);
    else if(0x9d==nIDFace)
        wcsncpy_s(face,8,L"[#炸弹#]",8);
    else if(0x89==nIDFace)
        wcsncpy_s(face,8,L"[#便便#]",8);
    else if(0xab==nIDFace)
        wcsncpy_s(face,8,L"[#委屈#]",8);
    else if(0x76==nIDFace)
        wcsncpy_s(face,8,L"[#抓狂#]",8);
    else if(0x74==nIDFace)
        wcsncpy_s(face,8,L"[#酷#]",8);
    else if(0x93==nIDFace)
        wcsncpy_s(face,8,L"[#嘘#]",8);
    else if(0x4a==nIDFace)
        wcsncpy_s(face,8,L"[#大哭#]",8);
    else if(0x46==nIDFace)
        wcsncpy_s(face,8,L"[#流泪#]",8);
    else if(0x8b==nIDFace)
        wcsncpy_s(face,8,L"[#可爱#]",8);
    else if(0x43==nIDFace)
        wcsncpy_s(face,8,L"[#色#]",8);
    else if(0x47==nIDFace)
        wcsncpy_s(face,8,L"[#害羞#]",8);
    else if(0x45==nIDFace)
        wcsncpy_s(face,8,L"[#得意#]",8);
    else if(0x77==nIDFace)
        wcsncpy_s(face,8,L"[#吐#]",8);
    else if(0x4f==nIDFace)
        wcsncpy_s(face,8,L"[#微笑#]",8);
    else if(0x4c==nIDFace)
        wcsncpy_s(face,8,L"[#发怒#]",8);
    else if(0x4b==nIDFace)
        wcsncpy_s(face,8,L"[#尴尬#]",8);
    else if(0x78==nIDFace)
        wcsncpy_s(face,8,L"[#惊怒#]",8);
    else if(0x49==nIDFace)
        wcsncpy_s(face,8,L"[#睡#]",8);
    else if(0x92==nIDFace)
        wcsncpy_s(face,8,L"[#疑问#]",8);
    else if(0x41==nIDFace)
        wcsncpy_s(face,8,L"[#惊讶#]",8);
    else if(0x73==nIDFace)
        wcsncpy_s(face,8,L"[#难过#]",8);
    else if(0x8d==nIDFace)
        wcsncpy_s(face,8,L"[#傲慢#]",8);
    else if(0x8c==nIDFace)
        wcsncpy_s(face,8,L"[#白眼#]",8);
    else if(0xb5==nIDFace)
        wcsncpy_s(face,8,L"[#示爱#]",8);
    else if(0x65==nIDFace)
        wcsncpy_s(face,8,L"[#爱心#]",8);
    else if(0xa1==nIDFace)
        wcsncpy_s(face,8,L"[#冷汗#]",8);
    else if(0xae==nIDFace)
        wcsncpy_s(face,8,L"[#亲亲#]",8);
    else if(0x7a==nIDFace)
        wcsncpy_s(face,8,L"[#憨笑#]",8);
    else if(0x5c==nIDFace)
        wcsncpy_s(face,8,L"[#爱情#]",8);
    else if(0x96==nIDFace)
        wcsncpy_s(face,8,L"[#衰#]",8);
    else if(0x42==nIDFace)
        wcsncpy_s(face,8,L"[#撇嘴#]",8);
    else if(0xad==nIDFace)
        wcsncpy_s(face,8,L"[#阴险#]",8);
    else if(0x90==nIDFace)
        wcsncpy_s(face,8,L"[#奋斗#]",8);
    else if(0x44==nIDFace)
        wcsncpy_s(face,8,L"[#发呆#]",8);
    else if(0xa8==nIDFace)
        wcsncpy_s(face,8,L"[#右哼哼#]",8);
    else if(0x70==nIDFace)
        wcsncpy_s(face,8,L"[#弱#]",8);
    else if(0x6f==nIDFace)
        wcsncpy_s(face,8,L"[#强#]",8);
    else if(0xb0==nIDFace)
        wcsncpy_s(face,8,L"[#可怜#]",8);
    else if(0x7b==nIDFace)
        wcsncpy_s(face,8,L"[#大兵#]",8);
    else if(0x94==nIDFace)
        wcsncpy_s(face,8,L"[#晕#]",8);
    else if(0xaa==nIDFace)
        wcsncpy_s(face,8,L"[#鄙视#]",8);
    else if(0x56==nIDFace)
        wcsncpy_s(face,8,L"[#飞吻#]",8);
    else if(0xa6==nIDFace)
        wcsncpy_s(face,8,L"[#坏笑#]",8);
    else if(0x7f==nIDFace)
        wcsncpy_s(face,8,L"[#拥抱#]",8);
    else if(0x88==nIDFace)
        wcsncpy_s(face,8,L"[#握手#]",8);
    else if(0xa0==nIDFace)
        wcsncpy_s(face,8,L"[#胜利#]",8);
    else if(0xb7==nIDFace)
        wcsncpy_s(face,8,L"[#抱拳#]",8);
    else if(0x63==nIDFace)
        wcsncpy_s(face,8,L"[#凋谢#]",8);
    else if(0x81==nIDFace)
        wcsncpy_s(face,8,L"[#饭#]",8);
    else if(0x67==nIDFace)
        wcsncpy_s(face,8,L"[#蛋糕#]",8);
    else if(0x61==nIDFace)
        wcsncpy_s(face,8,L"[#西瓜#]",8);
    else if(0xb2==nIDFace)
        wcsncpy_s(face,8,L"[#啤酒#]",8);
    else if(0xb6==nIDFace)
        wcsncpy_s(face,8,L"[#瓢虫#]",8);
    else if(0xb9==nIDFace)
        wcsncpy_s(face,8,L"[#拳头#]",8);
    else if(0xba==nIDFace)
        wcsncpy_s(face,8,L"[#差劲#]",8);
    else if(0x5a==nIDFace)
        wcsncpy_s(face,8,L"[#发抖#]",8);
    else if(0x9e==nIDFace)
        wcsncpy_s(face,8,L"[#刀#]",8);
    else if(0x6e==nIDFace)
        wcsncpy_s(face,8,L"[#月亮#]",8);
    else if(0x80==nIDFace)
        wcsncpy_s(face,8,L"[#咖啡#]",8);
    else if(0xbb==nIDFace)
        wcsncpy_s(face,8,L"[#爱你#]",8);
    else if(0xbd==nIDFace)
        wcsncpy_s(face,8,L"[#OK#]",8);
    else if(0xb8==nIDFace)
        wcsncpy_s(face,8,L"[#勾引#]",8);
    else if(0x66==nIDFace)
        wcsncpy_s(face,8,L"[#心碎#]",8);
    else if(0x6b==nIDFace)
        wcsncpy_s(face,8,L"[#太阳#]",8);
    else if(0x68==nIDFace)
        wcsncpy_s(face,8,L"[#礼物#]",8);
    else if(0x5e==nIDFace)
        wcsncpy_s(face,8,L"[#足球#]",8);
    else if(0x97==nIDFace)
        wcsncpy_s(face,8,L"[#骷髅#]",8);
    else if(0xc2==nIDFace)
        wcsncpy_s(face,8,L"[#挥手#]",8);
    else if(0x9c==nIDFace)
        wcsncpy_s(face,8,L"[#闪电#]",8);
    else if(0x8e==nIDFace)
        wcsncpy_s(face,8,L"[#饥饿#]",8);
    else if(0x8f==nIDFace)
        wcsncpy_s(face,8,L"[#困#]",8);
    else if(0xaf==nIDFace)
        wcsncpy_s(face,8,L"[#吓#]",8);
    else if(0xac==nIDFace)
        wcsncpy_s(face,8,L"[#快哭了#]",8);
    else if(0xa9==nIDFace)
        wcsncpy_s(face,8,L"[#哈欠#]",8);
    else if(0xa7==nIDFace)
        wcsncpy_s(face,8,L"[#左哼哼#]",8);
    else if(0xa5==nIDFace)
        wcsncpy_s(face,8,L"[#糗大了#]",8);
    else if(0xa4==nIDFace)
        wcsncpy_s(face,8,L"[#鼓掌#]",8);
    else if(0xa3==nIDFace)
        wcsncpy_s(face,8,L"[#抠鼻#]",8);
    else if(0x95==nIDFace)
        wcsncpy_s(face,8,L"[#折磨#]",8);
    else if(0x91==nIDFace)
        wcsncpy_s(face,8,L"[#咒骂#]",8);
    else if(0xb3==nIDFace)
        wcsncpy_s(face,8,L"[#篮球#]",8);
    else if(0xb4==nIDFace)
        wcsncpy_s(face,8,L"[#乒乓#]",8);
    else if(0xbc==nIDFace)
        wcsncpy_s(face,8,L"[#NO#]",8);
    else if(0x58==nIDFace)
        wcsncpy_s(face,8,L"[#跳跳#]",8);
    else if(0x5b==nIDFace)
        wcsncpy_s(face,8,L"[#怄火#]",8);
    else if(0xBE==nIDFace)
        wcsncpy_s(face,8,L"[#转圈#]",8);
    else if(0xBF==nIDFace)
        wcsncpy_s(face,8,L"[#磕头#]",8);
    else if(0xc0==nIDFace)
        wcsncpy_s(face,8,L"[#回头#]",8);
    else if(0xc1==nIDFace)
        wcsncpy_s(face,8,L"[#跳绳#]",8);
    else if(0x48==nIDFace)
        wcsncpy_s(face,8,L"[#闭嘴#]",8);
    else if(0xc7==nIDFace)
        wcsncpy_s(face,8,L"[#右太极#]",8);
    else if(0xc6==nIDFace)
        wcsncpy_s(face,8,L"[#左太极#]",8);
    else if(0xc5==nIDFace)
        wcsncpy_s(face,8,L"[#献吻#]",8);
    else if(0xc4==nIDFace)
        wcsncpy_s(face,8,L"[#街舞#]",8);
    else if(0xc3==nIDFace)
        wcsncpy_s(face,8,L"[#激动#]",8);
    else 
        return false;
    return true;
}

将上面说的综合起来就是本程序了,本程序的功能只负责将聊天记录导出到文本文档中,其实稍加修改就可以做到将20082009的聊天记录相互
转换。现在手机QQ出2010版了,不过只要腾讯它不像电脑上使用的QQ一样将聊天记录加密,应该也能分析出其格式从而将其导出或者转换。

如果哪位朋友发现程序中有错误或者不妥的地方希望能留言指出,谢谢……

本文链接http://element-ui.cn/news/show-592934.aspx