C++讀取UTF-8及GBK系列的文字方法及原理
1.讀取UTF-8編碼文字原理
首先了解UTF-8的編碼方式,UTF-8採用可變長編碼的方式,一個字元可佔1位元組-6位元組,其中每個字元所佔的位元組數由字元開始的1的個數確定,具體的編碼方式如下:
U-00000000 – U-0000007F: 0xxxxxxx
U-00000080 – U-000007FF: 110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此,對於每個位元組如果起始位為“0”則說明,該字元佔有1位元組。
如果起始位為“10”則說明該位元組不是字元的其實位元組。
如果起始為為n個“1”+1個“0”,則說明改字元佔有n個位元組。其中1≤n≤6。
因此對於UTF-8的編碼,我們只需要每次計算每個字元開始位元組的1的個數,就可以確定這個字元的長度。
2.讀取GBK系列文字原理
對於ASCII、GB2312、GBK到GB18030編碼方法是向下相容的 ,即同一個字元在這些方案中總是有相同的編碼,後面的標準支援更多的字元。
在這些編碼中,英文和中文可以統一地處理。區分中文編碼的方法是高位元組的最高位不為0。
因此我們只需處理好GB18130,就可以處理與他相容的所有編碼,對於GB18130使用雙位元組變長編碼。
單位元組部分從 0×0~0x7F 與 ASCII 編碼相容。雙位元組部分,首位元組從 0×81~0xFE,尾位元組從 0×40~0x7E以及 0×80~0xFE,與GBK標準基本相容。
因此只需檢測首位元組是否小於0×81即可確定其為單位元組編碼還是雙位元組編碼。
3.C++程式碼實現
對於一個語言處理系統,讀取不同編碼的文字應該是最基礎的需求,文字的編碼方式應該對系統其他呼叫者透明,只需每次獲取一個字元即可,而不需要關注這個文字的編碼方式。從而我們定義了抽象類Text,及其介面ReadOneChar,並使兩個文字類GbkText和UtfText繼承這個抽象類,當系統需要讀取更多種編碼的檔案時,只需要定義新的類然後繼承該抽象類即可,並不需要更改呼叫該類的程式碼。從而獲得更好的擴充套件性。
更好的方式是使用簡單工廠模式,使不同的文字編碼格式對於呼叫類完全透明,簡單工廠模式詳解請參看:C++實現設計模式之 — 簡單工廠模式
其中Text抽象類的定義如下:
#ifndef TEXT_H #define TEXT_H #include <iostream> #include <fstream> using namespace std; class Text { protected: char * m_binaryStr; size_t m_length; size_t m_index; public: Text(string path); void SetIndex(size_t index); virtual bool ReadOneChar(string &oneChar) = 0; size_t Size(); virtual ~Text(); }; #endif
Text抽象類的實現如下:
#include "Text.h" using namespace std; Text::Text(string path):m_index(0) { filebuf *pbuf; ifstream filestr; // 採用二進位制開啟 filestr.open(path.c_str(), ios::binary); if(!filestr) { cerr<<path<<" Load text error."<<endl; return; } // 獲取filestr對應buffer物件的指標 pbuf=filestr.rdbuf(); // 呼叫buffer物件方法獲取檔案大小 m_length=(int)pbuf->pubseekoff(0,ios::end,ios::in); pbuf->pubseekpos(0,ios::in); // 分配記憶體空間 m_binaryStr = new char[m_length+1]; // 獲取檔案內容 pbuf->sgetn(m_binaryStr,m_length); //關閉檔案 filestr.close(); } void Text::SetIndex(size_t index) { m_index = index; } size_t Text::Size() { return m_length; } Text::~Text() { delete [] m_binaryStr; }
GBKText類的定義如下:
#ifndef GBKTEXT_H #define GBKTEXT_H #include <iostream> #include <string> #include "Text.h" using namespace std; class GbkText:public Text { public: GbkText(string path); ~GbkText(void); bool ReadOneChar(string & oneChar); }; #endif
GBKText類的實現如下:
#include "GbkText.h" GbkText::GbkText(string path):Text(path){} GbkText::~GbkText(void) {} bool GbkText::ReadOneChar(string & oneChar) { // return true 表示讀取成功, // return false 表示已經讀取到流末尾 if(m_length == m_index) return false; if((unsigned char)m_binaryStr[m_index] < 0x81) { oneChar = m_binaryStr[m_index]; m_index++; } else { oneChar = string(m_binaryStr, 2); m_index += 2; } return true; }
UtfText類的定義如下:
#ifndef UTFTEXT_H #define UTFTEXT_H #include <iostream> #include <string> #include "Text.h" using namespace std; class UtfText:public Text { public: UtfText(string path); ~UtfText(void); bool ReadOneChar(string & oneChar); private: size_t get_utf8_char_len(const char & byte); }; #endif
UtfText類的實現如下:
#include "UtfText.h" UtfText::UtfText(string path):Text(path){} UtfText::~UtfText(void) {} bool UtfText::ReadOneChar(string & oneChar) { // return true 表示讀取成功, // return false 表示已經讀取到流末尾 if(m_length == m_index) return false; size_t utf8_char_len = get_utf8_char_len(m_binaryStr[m_index]); if( 0 == utf8_char_len ) { oneChar = ""; m_index++; return true; } size_t next_idx = m_index + utf8_char_len; if( m_length < next_idx ) { //cerr << "Get utf8 first byte out of input src string." << endl; next_idx = m_length; } //輸出UTF-8的一個字元 oneChar = string(m_binaryStr + m_index, next_idx - m_index); //重置偏移量 m_index = next_idx; return true; } size_t UtfText::get_utf8_char_len(const char & byte) { // return 0 表示錯誤 // return 1-6 表示正確值 // 不會 return 其他值 //UTF8 編碼格式: // U-00000000 - U-0000007F: 0xxxxxxx // U-00000080 - U-000007FF: 110xxxxx 10xxxxxx // U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx // U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx // U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx // U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx size_t len = 0; unsigned char mask = 0x80; while( byte & mask ) { len++; if( len > 6 ) { //cerr << "The mask get len is over 6." << endl; return 0; } mask >>= 1; } if( 0 == len) { return 1; } return len; }
工廠類TextFactory的類定義如下:
#ifndef TEXTFACTORY_H #define TEXTFACTORY_H #include <iostream> #include "Text.h" #include "UtfText.h" #include "GbkText.h" using namespace std; class TextFactory { public: static Text * CreateText(string textCode, string path); }; #endif
工廠類的實現如下:
#include "TextFactory.h" #include "Text.h" Text * TextFactory::CreateText(string textCode, string path) { if( (textCode == "utf-8") || (textCode == "UTF-8") || (textCode == "ISO-8859-2") || (textCode == "ascii") || (textCode == "ASCII") || (textCode == "TIS-620") || (textCode == "ISO-8859-5") || (textCode == "ISO-8859-7") ) { return new UtfText(path); } else if((textCode == "windows-1252") || (textCode == "Big5") || (textCode == "EUC-KR") || (textCode == "GB2312") || (textCode == "ISO-2022-CN") || (textCode == "HZ-GB-2312") || (textCode == "gb18030")) { return new GbkText(path); } return NULL; }
測試的Main函式如下:
#include <stdio.h> #include <string.h> #include <iostream> #include "Text.h" #include "TextFactory.h" #include "CodeDetector.h" using namespace std; int main(int argc, char *argv[]) { string path ="日文"; string code ="utf-8"; Text * t = TextFactory::CreateText(code, path); string s; while(t->ReadOneChar(s)) { cout<<s; } delete t; }
相關文章
- C++ 過濾出字串的中文(GBK,UTF-8)C++字串
- 幾種常見取樣方法及原理
- EXE檔案結構及讀取方法
- c++ istream 讀取方法C++
- C++ 多型的實現及原理C++多型
- PHP中文字元gbk編碼與UTF-8編碼的轉換PHP字元
- JS讀取本地TXT文字的兩種方法JS
- Nginx快取原理及機制Nginx快取
- HTTP快取機制及原理HTTP快取
- Java讀取資料夾大小的6種方法及程式碼Java
- Java集合 HashSet的原理及常用方法Java
- OkHttp 原始碼剖析系列(七)——請求的發起及響應的讀取HTTP原始碼
- TreeMap原理實現及常用方法
- PHP中文GBK編碼轉UTF-8PHP
- VB讀取文字檔案的例子:逐行讀取
- Lombok介紹,使用方法及原理Lombok
- Redis 快取穿透、快取雪崩原理及解決方案Redis快取穿透
- Unicode、GBK、UTF-8、ASCII的編碼簡介UnicodeASCII
- PHP 讀取CSV轉化為 UTF-8PHP
- Spring系列之IOC的原理及手動實現Spring
- Spring系列之AOP的原理及手動實現Spring
- Spring系列之DI的原理及手動實現Spring
- Java和Android的LRU快取及實現原理JavaAndroid快取
- 深入理解HTTP快取機制及原理HTTP快取
- 高效能快取 Caffeine 原理及實戰快取
- 最全的獲取元素寬高及位置的方法
- HTTP使用BASIC認證的原理及實現方法HTTP
- 光碟目錄隱藏原理及破解方法
- nmap os探測使用方法及原理
- 基於 Android 讀取微信本地 DB 資料 | 思維原理及技術分析Android
- 圖解B樹及C#實現(2)資料的讀取及遍歷圖解C#
- Express的使用及原理Express
- C++檔案說明及使用方法C++
- 使用jquery獲取url及url引數的方法jQuery
- Vue 進階系列(二)之外掛原理及實現Vue
- 轉:徹底弄懂HTTP快取機制及原理HTTP快取
- ProxySQL簡介原理及讀寫分離應用SQL
- Java 讀取Word文字框中的文字/圖片/表格Java