VC ANSI環境下按行讀取ANSI、UNICODE 、UNICODE big endian、UTF-8四種文字檔案
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_FILE static 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 char TextCode 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; else if(buf[0]==0xFF && buf[1]==0xFE ) tc=UNICODE ; else if(buf[0]==0xFE && buf[1]==0xFF ) tc=UNICODEBIGENDIAN; else tc=ANSI; } else tc=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效果不同 將省去回車符0x0d if(!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 here
char 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 here
if(!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 環境下的測試
相關推薦
VC ANSI環境下按行讀取ANSI、UNICODE 、UNICODE big endian、UTF-8四種文字檔案
1.問題提出 MFC提供的檔案類CStdioFile,其中一個函式ReadString實現了檔案的按行讀取,但是不能滿足不同型別的文字檔案的按行讀取,為了解決這一問題,筆者初步研究了一些編碼知識,參考了網上的一些資料,實現了CStdioFile類的擴充套件類CStdioFileEx,完成了常見文字檔案的按行讀
UNICODE環境下整行讀取文字檔案的方法
因為要逐行處理GB18030文字檔案,必須用UNICODE編碼。但在用ReadString讀取時總出錯,網上找了程式碼,但只能讀出前面5行,還有在文字比較上也不方便,因文字串末尾的回車符。琢磨了兩天終於完全搞定。下面是有關的程式碼,注意紅色部分。 ......... fil
VC 程式設計ANSI環境下讀寫Unicode檔案
沒有注意到檔案編碼的不同會產生這麼多的問題,在動手以前查詢了很多資料,在本部落格中收藏了不少先輩的成果,在這裡一併表示致敬! 關於ANSI和Unicode編碼的原理在這裡也不說了,主要講下如何讀寫! 首先確定你的工程是採用的是什麼編碼環境,預設是ANSI,不同的字符集讀
按行讀取String類型
類型 char logs ont log read pan nbsp span BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content.ge
python按行讀取並替換
tracking 技術分享 view popu tag sdn -c hat con fp = open(‘‘test2.txt‘,‘w‘) #打開你要寫得文件test2.txt lines = open(‘test1.txt‘).readlines() #
python 按行讀取判斷是否為空
span logs bsp 判斷 [0 mat adl () exce 1 for line in fr.readlines(): 2 try: 3 # print(len(line)) 4
在DOS環境下運行匯編程序
匯編匯編語言不像C語言一樣有夠多的運行平臺。今天講講如何在Dos下運行會匯編程序。首先安裝MASM5.0,如果是32位把壓縮包直接解壓就可以了,但是64位需要在建立特定的文件夾存放.asm文件,因為我的電腦是win7 64位的所以介紹我的做法吧。我的文件夾存放於D盤叫做myass,在此文件夾建立文本文件,寫好
DataFrame按行讀取:DataFrame之values
技術 類型 ash 查看 nbsp 9.4 range 所有 div http://blog.csdn.net/u014607457/article/details/51290582 def fill_core(self): Ra
按行讀取文本或字符串到數組效率測試:StreamReader與Split函數對比
sed ade csv tel style null con nco str 1. 讀取文本文件測試:測試文件“XX.csv”,3538行 耗時:4618ms Stopwatch sw = new Stopwatch();
新安裝的nginx環境下運行tp框架路由不能用的問題
pass cati 這一 start 框架 clas param fff nbsp location ~ \.php { #去掉$ root H:/PHPServer/WWW; 筆者這裏默認是 html; fastcg
CentOS7環境下命令行tab鍵盤補全
table .sh summary 軟件包 logo nvidia mpc connect 顯示 一般情況下CentOS7默認最小化安裝,此時如果要命令行tab補全需要安裝一個叫做“bash-completion” 的軟件包 沒有安裝bash-completion之前 1
在pycharm和tensorflow環境下運行nmt
light enc -o arm mas 環境 編譯 -- charm 目的是在pycharm中調試nmt代碼,主要做了如下工作: 配置pycharm編譯環境 在File->Settings->Project->Project Interpreter 設置
編譯器可以生成相同的環境下運行的目標代碼
計算 nbsp ++ 轉換 編譯器 amp all public tab 編譯器可以生成用來在與編譯器本身所在的計算機和操作系統(平臺)相同的環境下運行的目標代碼 ,這種編譯器又叫做“本地”編譯器。 另外,編譯器也可以生成用來在其它平臺上運行的目標代碼,這種編譯器又叫做交
MFC-按行讀取TXT數據
std dword 讀取字符串 數據 get 按行讀取 mod cstring 文件 TXT中數據格式如下: 1 23 4 0 4 10 …… 要實現的功能是:定義一個函數,每次調用時從TXT文檔中讀一個整數 ,賦值給變量。同時,文件位置向下移動一行,以便下次調用時
Java文字檔案的按行讀取
package my; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.InputStrea
C 按行讀取檔案(但是最後一行會多輸出一行)
#include <stdio.h> int main() { char filename[] = "E:\\data_test\\commands.txt"; //檔名 &nb
HDFS Java API按行讀取
2018.10.28 文章目錄 前言 方法 前言 翻譯自stackoverflow一回答1 方法 public void test() throws Exception { Path path
java按行讀取檔案並對檔案進行加密和解密
package com.alibaba.datax.plugin.reader.selfxmlfilereader.util; import java.io.UnsupportedEncodingException; import java.security
Python之按行讀取DataFrame二維陣列資料
import numpy as np import pandas as pd data = {"省份":['北京', "上海", "天津", "重慶", "江蘇", "浙江", "廣東"], "年份":[2017, 2015, 2013, 2016, 200
20170928 使用網頁管理叢集 按行讀取寫入useragent txt檔案
根據20170901---20170903的position資料分析useragent,優化urlutil。 從叢集中hdfs dfs -cat /user/log/position/20170901/*/* |awk -Furlutil'\t' 'print $8'|sort|uniq >