源文件下载地址: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;
}
将上面说的综合起来就是本程序了,本程序的功能只负责将聊天记录导出到文本文档中,其实稍加修改就可以做到将2008和2009的聊天记录相互
转换。现在手机QQ出2010版了,不过只要腾讯它不像电脑上使用的QQ一样将聊天记录加密,应该也能分析出其格式从而将其导出或者转换。
如果哪位朋友发现程序中有错误或者不妥的地方希望能留言指出,谢谢……

VC写的手机qq聊天记录导出工具相关推荐

  1. 微信聊天记录导出工具WeChatExporter开源啦!

    [2019年08月21日更新] 距离第一次发布软件已经有了许多新功能和稳定性上的提升,本文的一些内容已经过时,欢迎直接到GitHub上看ReadMe:https://github.com/tsycnh ...

  2. 手机qq 聊天记录 同步到电脑qq上

    手机QQ 可以导出聊天记录为txt格式,但是电脑pcQQ 貌似不支持. 发现手机QQ有个同步功能,同步最近聊天记录. 电脑上也有. 普通 QQ用户可以漫游7天的好友聊天记录,不包括图片.这样,七天内, ...

  3. java解密手机QQ聊天记录

    转载链接:http://blogjava.sinaapp.com/?p=38 手机QQ聊天记录需要手机root才可以获取到,这是个鸡肋,但是网上还有许多想解密的,还有收费的.如果查小三,估计可以有这么 ...

  4. 微信QQ聊天记录分析工具-微Q

    1.背景 忙了一周,从设计算法到编程,到部署服务器,到最后的UI实现,终于我的微Q诞生了. 用起来非常的简洁,只要把微信或者qq的聊天记录导出来,是个txt文件,导入微Q,手机端也能用,它就能帮你分析 ...

  5. Android手机资料拷贝导出工具 --- 91手机助手

    http://zs.91.com/ 转载于:https://www.cnblogs.com/onelikeone/p/9869129.html

  6. jq 仿手机qq聊天记录滑动删除

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  7. 手机QQ2009聊天记录分析及提取

    十月十三号用上手机QQ2009了,一看,果然更花哨了.但以前写的手机QQ聊天记录提取程序用不上了,莫办法,重新分析. 一 手机QQ2009聊天记录文件的存放位置 C:/System/data/Tenc ...

  8. 华为手机连电脑_华为手机微信聊天记录如何导出电脑的四大方法

    华为手机是国内销量最大的国产手机,很多人想把华为手机微信聊天记录导出到电脑上进行保存,或进行打印,却不知道如何操作.其实有四种方法可以把华为手机上的微信聊天记录导出到电脑上,包括聊天记录中的所有文字, ...

  9. qq聊天记录词频查询 python实现

    为了给后期聊天机器人提供大量的聊天词汇写了这个脚本,感兴趣的朋友可以看一下 下面是代码: #-*-coding:utf8-*- import re def getWordRate(name,path, ...

  10. C#开发的高性能EXCEL导入、导出工具DataPie(支持MSSQL、ORACLE、ACCESS,附源码下载地址)...

    作 为财务数据核算人员,面对大量的业务与财务数据,借助于传统的EXCEL表格,已经力不从心.最近几个月,利用周末及下班的空闲时间,写了一个数据库导入 导出工具,以方便业务逻辑密集型的数据处理.目前,D ...

最新文章

  1. 2022-2028年中国三网融合产业深度调研及投资前景预测报告
  2. LYNC2013部署系列PART4:群聊部署
  3. oracle数据泵导入分区表,Oracle 10g 数据泵分区表的导出
  4. Vue最全知识点,面试必备(基础到进阶,覆盖vue3.0,持续更新整理,欢迎补充讨论)
  5. asp.net AJAX 使用webServices调用时,出现“WebService”未定义
  6. nginx配置ssl证书的方法
  7. C# 调用控制台应用程序及传参
  8. postman下载安装和基础使用教程(官网)
  9. Blender3.0资产浏览器
  10. mxnet symbol 解析
  11. 解决安卓CPU使用率过高问题
  12. linux识别riser卡,Riser卡和PCIe槽位
  13. 深度解析上海互联网产业为何沉沦
  14. 汉堡包菜单_神圣的汉堡包!
  15. LR参数化,参数化类型:Fille类型--2列多个参数
  16. 北京喜意来误请“熊猫烧香”骗子团伙“毒王”解决password01.txt.shs病毒(图)
  17. 方格1010+颠覆经典俄罗斯方块逻辑玩法的手游
  18. python表单验证wtf_关于python 3.x:无法验证Flask WTF-Form
  19. 关于创建问卷调查类型,跳题顺延排序的实现
  20. HTML创意菜单设计,有特色的使用大菜单的网页设计

热门文章

  1. week15 作业哈希算法
  2. idea安装jclasslib和BindEd,以及使用
  3. python实数符号_下列格式化符号中,用来表示浮点实数的是()。 (6.0分)_学小易找答案...
  4. 方程检验格式图片_eviews的异方差检验ppt课件
  5. 【STMT】等价类划分法
  6. 前端遇到GET https://XXXX net::ERR_HTTP2_PROTOCOL_ERROR 200问题的解决办法
  7. Html 排版与标签(一)
  8. pr 导出视频 黑屏 或者 没有画面
  9. C#基于FFmpeg实现录屏功能
  10. box-sizing 的使用