VC ANSI环境下按行读取ANSI、UNICODE 、UNICODE big endian、UTF-8四种文本文件
来源:互联网 发布:计算机系网络pdf谢希仁 编辑:程序博客网 时间:2024/06/05 09:07
VC ANSI环境下按行读取ANSI、UNICODE 、UNICODE big endian、UTF-8四种文本文件
1.问题提出
MFC提供的文件类CStdioFile,其中一个函数ReadString实现了文件的按行读取,但是不能满足不同类型的文本文件的按行读取,为了解决这一问题,笔者初步研究了一些编码知识,参考了网上的一些资料,实现了CStdioFile类的扩展类CStdioFileEx,完成了常见文本文件的按行读取(注明:不包括DOC、PDF等其他形式的文档).
在此对网上分享编码经验的网友表示感谢,同时由于我编写的类还未经过严格测试,如有错误或方法过于复杂敬请各位指正。
2.问题解决
(1)四种常见文本文件编码方式研究
ANSI、UNICODE 、UNICODE big endian、UTF-8四种格式编码存在差别,简要介绍如下:
ANSI编码:
无文件头(文件编码开头标志性字节)
ANSI编码字母数字占一个字节,汉字占两个字节,
回车换行符 单字节 十六进制表示为0d 0a
UNICODE编码:
文件头,十六进制表示为FF FE
每一个字符都用两个字节编码
回车换行符 双字节 000d 000a
Unicode big endian编码:
文件头十六进制表示为FE FF ,
后面编码是把字符的高位放在前面,低位放在后面,正好和Unicode编码颠倒。
回车换行符,双字节,十六进制表示为0d00 0a00
UTF-8 编码:
文件头,十六进制表示为EF BB BF。
UTF-8是Unicode的一种变长字符编码,数字、字母、回车、换行都用一个字节表示,汉字占3个字节.
回车换行符,单字节,十六进制表示为0d 0a
以中文"你好"二字为例,各种类型的编码对应的十六进制格式(可由EditPlus查看)如下图所示:
由此可见上述的探讨是正确的。
(2)按行读取上述四种格式文本文件的解决方案
针对不同文件编码的特点,通过先检测文件头判断文件编码类型,然后根据文件类型分别调用不同的读取函数实现文件的按行读取。按行读取过程如下图所示:
实现过程中,编写CStdioFileEx类,该类继承自CStdioFile类,覆盖了CStdioFile类的BOOL ReadString(CString& rString)方法,从而实现了文件按行读取。
(3)CStdioFileEx类的实现代码
代码清单:
3.运行结果
(1)测试部分核心代码
//打开文件
程序在Windows 7 VC6.0下测试通过,测试结果如下图所示:
ANSI文件测试结果:
UNICODE文件测试结果:
UNICODE big endian文件测试结果:
UTF-8文件测试结果:
4.尚未解决的问题
(1)CStdioFileEx 类未重载CStdioFile的 virtual LPTSTR ReadString( LPTSTR lpsz, UINT nMax )方法
(2)未完成CStdioFileEx 类的WriteString方法
(3)未在VC UNICODE 环境下的测试CStdioFileEx 类
(4)是否存在更简洁、安全的方法?因为笔者研究编码目前还不深入,因此还未能得到更好的解答。
5.感谢
感谢网上分享的材料,参考资料列表:
(1)《关于编码的小测试 》 网址: http://www.live-in.org/archives/277.html
(2) CStdioFileEx类
下载网址:http://download.pudn.com/downloads141/sourcecode/windows/file/21457326wutext.rar
(3)《C++字符串完全指引之一 Win32 字符编码》 网址:http://www.vckbase.com/document/viewdoc/?id=1082
MFC提供的文件类CStdioFile,其中一个函数ReadString实现了文件的按行读取,但是不能满足不同类型的文本文件的按行读取,为了解决这一问题,笔者初步研究了一些编码知识,参考了网上的一些资料,实现了CStdioFile类的扩展类CStdioFileEx,完成了常见文本文件的按行读取(注明:不包括DOC、PDF等其他形式的文档).
在此对网上分享编码经验的网友表示感谢,同时由于我编写的类还未经过严格测试,如有错误或方法过于复杂敬请各位指正。
2.问题解决
(1)四种常见文本文件编码方式研究
ANSI、UNICODE 、UNICODE big endian、UTF-8四种格式编码存在差别,简要介绍如下:
ANSI编码:
无文件头(文件编码开头标志性字节)
ANSI编码字母数字占一个字节,汉字占两个字节,
回车换行符 单字节 十六进制表示为0d 0a
UNICODE编码:
文件头,十六进制表示为FF FE
每一个字符都用两个字节编码
回车换行符 双字节 000d 000a
Unicode big endian编码:
文件头十六进制表示为FE FF ,
后面编码是把字符的高位放在前面,低位放在后面,正好和Unicode编码颠倒。
回车换行符,双字节,十六进制表示为0d00 0a00
UTF-8 编码:
文件头,十六进制表示为EF BB BF。
UTF-8是Unicode的一种变长字符编码,数字、字母、回车、换行都用一个字节表示,汉字占3个字节.
回车换行符,单字节,十六进制表示为0d 0a
以中文"你好"二字为例,各种类型的编码对应的十六进制格式(可由EditPlus查看)如下图所示:
由此可见上述的探讨是正确的。
(2)按行读取上述四种格式文本文件的解决方案
针对不同文件编码的特点,通过先检测文件头判断文件编码类型,然后根据文件类型分别调用不同的读取函数实现文件的按行读取。按行读取过程如下图所示:
实现过程中,编写CStdioFileEx类,该类继承自CStdioFile类,覆盖了CStdioFile类的BOOL ReadString(CString& rString)方法,从而实现了文件按行读取。
(3)CStdioFileEx类的实现代码
代码清单:
// StdioFileEx.h: interface for the CStdioFileEx class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_STDIOFILEEX_H__C1F1F96B_9417_4388_8D24_892EDFA2A616__INCLUDED_)#define AFX_STDIOFILEEX_H__C1F1F96B_9417_4388_8D24_892EDFA2A616__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000// --------------------------------------------------------------------------------------------//程序用途:按行读取常见(包括ANSI、UNICODE、UNICODE big endian、UTF-8)格式的文本文件 //程序作者:湖北师范学院计算机科学与技术学院 王定桥 //核心算法:CStdioFileEx继承自CStdioFile, 覆盖CStdioFile的 BOOL ReadString(CString& rString)方法, // 根据不同文件编码特征,寻找文件回车换行符判断读取行结束,文件结束符判断文件结束 // 检测不同文件编码头部,获取文件类型后调用不同的读取函数//测试结果:在Windows7 VC6.0环境下测试上述四种格式的txt文件通过//尚未完成:未重载CStdioFile的 virtual LPTSTR ReadString( LPTSTR lpsz, UINT nMax )方法// 未完成WriteString方法,未在VC UNICODE 环境下的测试 //制作时间:2012-04-19 //代码版权:代码公开供学习交流使用 欢迎指正错误 改善算法 // --------------------------------------------------------------------------------------------#include "stdafx.h"//文本文件类型枚举值typedef enum TextCodeType{UTF8=0,UNICODE =1,UNICODEBIGENDIAN=2,ANSI=3,FILEERROR=4}TextCode;class CStdioFileEx :public CStdioFile{public:CStdioFileEx();CStdioFileEx(FILE* pOpenStream);CStdioFileEx(LPCTSTR lpszFileName, UINT nOpenFlags);virtual ~CStdioFileEx();virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL);public://文件类型值转换到字符串CString FileTypeToString();//获取文件类型TextCode GetFileType();//按行读取文件BOOL ReadString(CString& rString);//静态方法 获取文件类型static TextCode GetFileType( LPCTSTR lpszFileName);protected:TextCode m_FileType;//保存文件类型const static int PREDEFINEDSIZE;//预定义一行文件所需空间protected://从UTF-8文件按行读取BOOL ReadStringFromUTF8File(CString& rString);//从ANSI文件按行读取 BOOL ReadStringFromAnsiFile(CString& rString);//重UNCIDOE、UNICODE big endian文件读取BOOL ReadStringFromUnicodeFile(CString& rString);//UTF-8字符串转换到UNICODE字符串CString UTF8ToUnicode(byte *szUTF8);//处理文件打开标志UINT ProcessFlags(LPCTSTR lpszFileName, UINT& nOpenFlags,TextCode &tc);};#endif // !defined(AFX_STDIOFILEEX_H__C1F1F96B_9417_4388_8D24_892EDFA2A616__INCLUDED_)
// StdioFileEx.cpp: implementation of the CStdioFileEx class.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include "StdioFileEx.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Construction/Destruction///////////////////////////////////////////////////////////////////////*static*/ const int CStdioFileEx::PREDEFINEDSIZE=1024;CStdioFileEx::CStdioFileEx():CStdioFile(){m_FileType=ANSI;//指定默认类型}CStdioFileEx::CStdioFileEx(FILE* pOpenStream):CStdioFile(pOpenStream){ CString filepath=pOpenStream->_tmpfname;//? 尚不清楚File*结构 m_FileType=GetFileType(filepath);}CStdioFileEx::CStdioFileEx(LPCTSTR lpszFileName, UINT nOpenFlags):CStdioFile(lpszFileName,ProcessFlags(lpszFileName, nOpenFlags,m_FileType) ){}CStdioFileEx::~CStdioFileEx(){}// --------------------------------------------------------------------------------------------//CStdioFileEx::GetFileType 静态方法 检测文本文件类型// --------------------------------------------------------------------------------------------/*static */ TextCode CStdioFileEx::GetFileType(LPCTSTR lpszFileName){ CFile file;byte buf[3];//unsigned charTextCode tc;try{if(file.Open(lpszFileName,CFile::modeRead|CFile::shareDenyNone|CFile::typeBinary)){ file.Read(buf,3);if(buf[0]==0xEF && buf[1]==0xBB && buf[2]==0xBF)tc=UTF8;elseif(buf[0]==0xFF && buf[1]==0xFE )tc=UNICODE ;elseif(buf[0]==0xFE && buf[1]==0xFF )tc=UNICODEBIGENDIAN;elsetc=ANSI;}elsetc=FILEERROR;}catch (CFileException ex){ CString errormsg;errormsg.Format(_T("操作文件%s时发生异常!"),ex.m_strFileName); AfxMessageBox(errormsg);}return tc;}// --------------------------------------------------------------------------------------------//CStdioFileEx::Readstring 按行读取文本文件//根据不同文件类型 调用不同的读取函数// --------------------------------------------------------------------------------------------BOOL CStdioFileEx::ReadString(CString& rString){ BOOL flag=FALSE; switch(m_FileType) { case ANSI: flag=ReadStringFromAnsiFile(rString); break; case UNICODE: case UNICODEBIGENDIAN: flag=ReadStringFromUnicodeFile(rString); break; case UTF8: flag=ReadStringFromUTF8File(rString); break; case FILEERROR: flag=FALSE; break; default: break; } return flag;}// --------------------------------------------------------------------------------------------//CStdioFileEx::ReadstringFromAnsiFile 从ANSI文件读取字符串// --------------------------------------------------------------------------------------------BOOL CStdioFileEx::ReadStringFromAnsiFile(CString& rString){ BOOL flag;try{ flag=CStdioFile::ReadString(rString);rString+="\r\n";}catch(CFileException ex){CString errormsg;errormsg.Format(_T("操作文件%s时发生异常!"),ex.m_strFileName); AfxMessageBox(errormsg);} return flag;}// --------------------------------------------------------------------------------------------//CStdioFileEx::ReadstringFromUTF8File 从UTF8文件中按行读取 //由于UTF-8编码多字节编码且各种字符长度不同,判断回车换行需要判断连续两个字节 // --------------------------------------------------------------------------------------------BOOL CStdioFileEx::ReadStringFromUTF8File(CString& rString){long index;byte cr=0x0d;//回车换行符byte lf=0x0a;byte temp[2];byte tempbyte;byte *pbuf=new byte[PREDEFINEDSIZE+1];memset(pbuf,0,(PREDEFINEDSIZE+1)*sizeof(byte));UINT readlen;try{ //跳过文件头 移动文件指针if (m_pStream && ( GetPosition() == 0)){CStdioFile::Seek(3*sizeof(byte),CFile::begin);}index=0;do {memset(temp,0,2*sizeof(byte));readlen=CFile::Read(temp,2);//CStdioFile::Read效果不同 将省去回车符0x0dif(!readlen)return FALSE;//元素存贮到字节数组中pbuf[index++]=temp[0];pbuf[index++]=temp[1];tempbyte=temp[1];//判断回车换行if( ( tempbyte==cr && temp[0]==lf) ||(temp[0]==cr && temp[1]==lf))break;} while (readlen==2 && index<PREDEFINEDSIZE );pbuf[index]=0;rString=UTF8ToUnicode(pbuf);//UTF8编码转换到UNICODE}catch (CFileException ex){CString errormsg;errormsg.Format(_T("操作文件%s时发生异常!"),ex.m_strFileName); AfxMessageBox(errormsg);} delete[] pbuf;return TRUE;}// --------------------------------------------------------------------------------------------//从UNICODE、UNICODE big endian文件按行读取//当读取字节小于请求值(文件结束)或者超过预定义空间时无条件退出循环//wChLine存放每行字符,wchtemp存放临时读取字符//当编码为UNICODE big endian 时交换高低字节 ,将其转换成UNICODE字符串// --------------------------------------------------------------------------------------------BOOL CStdioFileEx::ReadStringFromUnicodeFile(CString& rString){long index;UINT readlen;wchar_t wchcr=MAKEWORD(0x0d,0x00);;//回车符 MakeWord(低、高字节顺序) wchar_t wchlf=MAKEWORD(0x0a,0x00);wchar_t *wChLine=new wchar_t[PREDEFINEDSIZE+1];memset(wChLine,0,(PREDEFINEDSIZE+1)*sizeof(wchar_t));wchar_t wchtemp[2];BOOL flag=TRUE;try{ //跳过文件头 移动文件指针if (m_pStream && ( GetPosition() ==0)){Seek(2*sizeof(byte),CFile::begin);}index=0;do{ memset(wchtemp,0,2*sizeof(wchar_t));readlen=CFile::Read(wchtemp,sizeof(wchar_t)*2);//CStdioFile::Read效果不同if(!readlen) break;//UNICODE big endian交换高低字节if(UNICODEBIGENDIAN==m_FileType){ unsigned char high, low;high = (wchtemp[0] & 0xFF00) >>8;low = wchtemp[0] & 0x00FF;wchtemp[0] = ( low <<8) | high;high = (wchtemp[1] & 0xFF00) >>8;low = wchtemp[1] & 0x00FF;wchtemp[1] = ( low <<8) | high;}wChLine[index++]=wchtemp[0];wChLine[index++]=wchtemp[1];//判断回车换行if(wchtemp[0]==wchcr && wchtemp[1]==wchlf)break;}while( (readlen==sizeof(wchar_t)*2) && index<PREDEFINEDSIZE );wChLine[index]=0;CString strtext(wChLine,index);rString=strtext;if(rString.IsEmpty()) flag=FALSE;}catch (CFileException ex){CString errormsg;errormsg.Format(_T("操作文件%s时发生异常!"),ex.m_strFileName); AfxMessageBox(errormsg);}delete[] wChLine;return flag;}// --------------------------------------------------------------------------------------------//CStdioFileEx::UTF8ToUnicode UTF-8字符串转换成UNICODE字符串// --------------------------------------------------------------------------------------------CString CStdioFileEx::UTF8ToUnicode(byte *szUTF8){ CString strret; strret=_T("");if(!szUTF8)return strret;//获取转换后所需串空间的长度 int wcsLen = MultiByteToWideChar(CP_UTF8,0,(LPSTR)szUTF8,strlen((char*)szUTF8),NULL,0);LPWSTR lpw=new WCHAR[wcsLen+1]; if(!lpw)return strret;memset(lpw,0,(wcsLen+1)*sizeof(wchar_t)); //实施转换MultiByteToWideChar(CP_UTF8,0, (LPSTR)szUTF8, strlen((char *)szUTF8), (LPWSTR)lpw, wcsLen); CString str(lpw);delete[] lpw;return str;}// --------------------------------------------------------------------------------------------//CStdioFileEx::GetFileType获取文件类型// --------------------------------------------------------------------------------------------TextCode CStdioFileEx::GetFileType(){ return m_FileType;}// --------------------------------------------------------------------------------------------//CStdioFileEx::FileTypeToString 文件类型枚举值转换为字符串值// --------------------------------------------------------------------------------------------CString CStdioFileEx::FileTypeToString(){ CString strtype;switch(m_FileType){case ANSI:strtype.Format("%s",_T("ANSI"));break;case UTF8:strtype.Format("%s",_T("UTF8"));break;case UNICODE:strtype.Format("%s",_T("UNICODE"));break;case UNICODEBIGENDIAN:strtype.Format("%s",_T("UNICODE big endian"));break;case FILEERROR:strtype.Format("%s",_T("FILEERROR"));break;default:break;}return strtype;}// --------------------------------------------------------------------------------------------//CStdioFileEx::Open 重载父类的文件打开操作 改变不同类型文件的打开方式// --------------------------------------------------------------------------------------------BOOL CStdioFileEx::Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError){ ProcessFlags(lpszFileName,nOpenFlags,m_FileType);//处理文件打开方式return CStdioFile::Open(lpszFileName, nOpenFlags,pError);}// --------------------------------------------------------------------------------------------//CStdioFileEx::ProcessFlags 处理不同文件的打开方式//ANSI文件采用文本读取,UNICODE、UNICDOE big endian、UTF-8采用二进制方式读取// --------------------------------------------------------------------------------------------UINT CStdioFileEx::ProcessFlags(LPCTSTR lpszFileName, UINT& nOpenFlags,TextCode &tc){ tc=CStdioFileEx::GetFileType(lpszFileName);if ((nOpenFlags & CFile::modeReadWrite)|| (nOpenFlags & CFile::modeRead) ){switch(tc){case ANSI:nOpenFlags|= CFile::typeText;nOpenFlags&=~CFile::typeBinary;break;case UTF8:nOpenFlags |= CFile::typeBinary;nOpenFlags&= ~CFile::typeText;break;case UNICODE:nOpenFlags |= CFile::typeBinary;nOpenFlags&= ~CFile::typeText;break;case UNICODEBIGENDIAN:nOpenFlags |= CFile::typeBinary;nOpenFlags&= ~CFile::typeText;break;case FILEERROR:break;default:break;}}nOpenFlags|=CFile::shareDenyNone;return nOpenFlags;}
3.运行结果
(1)测试部分核心代码
//打开文件
void CReadStringDlg::OnBtnOpen() {// TODO: Add your control notification handler code herechar szFilter[] = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||";CFileDialog filedlg(TRUE , "txt", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter,this); if(IDOK==filedlg.DoModal()){ m_strPath=filedlg.GetPathName();UpdateData(FALSE);m_ctrlEdit.SetSel(0,-1);m_ctrlEdit.Clear();if(m_stdiofileex.Open(m_strPath,CFile::modeRead)){m_strFileType=m_stdiofileex.FileTypeToString();UpdateData(FALSE);}else{MessageBox(_T("读取文件失败!"));}}}//读取文件void CReadStringDlg::OnBtnRead() {// TODO: Add your control notification handler code hereif(!ValidateInput())return;CString strread,strtemp;m_ctrlEdit.GetWindowText(strread);m_ctrlEdit.SetSel(0,-1);m_ctrlEdit.Clear();if(m_stdiofileex.m_pStream){ int cnt=0;strread+="\r\n";while(cnt<m_lLineCnt){if(m_stdiofileex.ReadString(strtemp)){ strread+=strtemp;cnt++;}else{ AfxMessageBox(_T("读取已经到达文件末尾!"));break;}}m_ctrlEdit.SetSel(0,-1);m_ctrlEdit.ReplaceSel(strread);}else{MessageBox(_T("读取文件失败!"));}}//验证输入BOOL CReadStringDlg::ValidateInput(){ UpdateData();if(m_strPath.IsEmpty()){MessageBox("文件路径为空,请填写!");return FALSE;}if(m_lLineCnt<=0)return FALSE;return TRUE;}(2)测试结果
程序在Windows 7 VC6.0下测试通过,测试结果如下图所示:
ANSI文件测试结果:
UNICODE文件测试结果:
UNICODE big endian文件测试结果:
UTF-8文件测试结果:
4.尚未解决的问题
(1)CStdioFileEx 类未重载CStdioFile的 virtual LPTSTR ReadString( LPTSTR lpsz, UINT nMax )方法
(2)未完成CStdioFileEx 类的WriteString方法
(3)未在VC UNICODE 环境下的测试CStdioFileEx 类
(4)是否存在更简洁、安全的方法?因为笔者研究编码目前还不深入,因此还未能得到更好的解答。
5.感谢
感谢网上分享的材料,参考资料列表:
(1)《关于编码的小测试 》 网址: http://www.live-in.org/archives/277.html
(2) CStdioFileEx类
下载网址:http://download.pudn.com/downloads141/sourcecode/windows/file/21457326wutext.rar
(3)《C++字符串完全指引之一 Win32 字符编码》 网址:http://www.vckbase.com/document/viewdoc/?id=1082
- VC ANSI环境下按行读取ANSI、UNICODE 、UNICODE big endian、UTF-8四种文本文件
- 按行读取ANSI、UNICODE 、UNICODE big endian、UTF-8四种文本文
- ANSI UNIcode UNIcode big endian UTF-8
- unicode,ansi,utf-8,unicode big endian
- J2ME读取本地文本文件(ANSI,Unicode,Unicode big endian,UTF-8编码)
- Unicode/Ansi/UTF-8/Big Endian
- ANSI、UTF-8、Unicode(little endian)、Unicode big endian
- ANSI、Unicode、Unicode big endian、UTF-8四种编码方式的对比
- [转载]Unicode、ANSI、UTF-8、Unicode Big Endian的故事
- Unicode、ANSI、UTF-8、Unicode Big Endian的故事
- Unicode、ANSI、UTF-8、Unicode Big Endian的故事
- unicode,ansi,utf-8,unicode big endian的区别
- Unicode,ANSI,UTF-8,Unicode Big Endian的故事
- unicode,ansi,utf-8,unicode big endian的故事
- unicode,ansi,utf-8,unicode big endian编码的区别
- unicode,ansi,utf-8,unicode big endian的故事
- 介绍一下unicode,ansi,utf-8,unicode big endian编码
- 介绍一下unicode,ansi,utf-8,unicode big endian编码
- PHP正则表达式经验
- iPhone开发内存管理
- GCC最基本的几个用法
- 虚拟主机 VirtualHost 配置
- android jni 基本数据类型 类 复杂数据类型 ArrayList
- VC ANSI环境下按行读取ANSI、UNICODE 、UNICODE big endian、UTF-8四种文本文件
- 正则表达式转义字符表
- Java API的概、常用类简介
- PL/SQL7——变量与SQL*Plus环境设置
- bzoj2656
- Objective-C 的基本数据类型、数字、字符串和集合等介绍
- 实训心得
- git log 小结
- mysql java使用PreparedStatement插入数据并返回id的代码