2017-03-05 109 views
1

儘管在論壇上關於unicode和字符串轉換(在C/C++中)以及谷歌搜索了幾個小時的話題,仍然找不到對我看來像是一個非常基本的過程的直接解釋。這是我想要做的:字符串到Unicode和Unicode到十進制代碼點(C++)

  • 我有一個字符串,它可能使用任何可能的語言的任何字符。以西里爾文爲例。所以說我有: std::string str = "сапоги";

  • 我想遍歷每個字符組成該字符串和:

    • 知道/打印字符的Unicode值
    • 轉換是Unicode值的十進制值

我真的谷歌搜索幾個小時,並找不到直接的答案。如果有人能告訴我如何做到這一點,那會很好。

編輯

所以我設法得到那麼遠:

#include <cstdlib> 
#include <cstdio> 
#include <iostream> 
#include <locale> 
#include <codecvt> 
#include <iomanip> 

// utility function for output 
void hex_print(const std::string& s) 
{ 
    std::cout << std::hex << std::setfill('0'); 
    for(unsigned char c : s) 
     std::cout << std::setw(2) << static_cast<int>(c) << ' '; 
    std::cout << std::dec << '\n'; 
} 

int main() 
{ 
    std::wstring test = L"сапоги"; 

    std::wstring_convert<std::codecvt_utf16<wchar_t>> conv1; 
    std::string u8str = conv1.to_bytes(test); 
    hex_print(u8str); 

    return 1; 
} 

結果:

04 41 04 30 04 3f 04 3e 04 33 04 38 

Code

哪個是正確的(它映射到Unicode)。問題是我不知道我是否應該使用utf-8,16或其他內容(正如克里斯在評論中指出的那樣)。有沒有一種方法可以找到這個問題? (無論編碼它最初使用或需要使用的任何編碼?)

EDIT 2

我想我會解決一些與第二編輯評論:

「轉換的是Unicode值到十進制值「爲什麼?

我會解釋爲什麼,但我也想以友好的方式發表評論,我的問題不是'爲什麼',而是'如何';-)。你可以假設OP有提出這個問題的理由,但是當然,我知道人們爲什麼好奇......所以讓我解釋一下。我之所以需要這一切,是因爲我最終需要從字體文件中讀取字形(TrueType OpenType無關緊要)。碰巧這些文件有一個名爲cmap的表,它是某種類型的關聯數組,將字符的值(在代碼點上的表單中)映射到字體文件中字形的索引。表中的代碼點沒有使用符號U + XXXX定義,而是直接在該數字的小數對應中定義(假設U + XXXX表示法是uint16數字的十六進制表示法[或者如果大於uint16,則爲U + XXXXXX但更多在那之後])。因此總之,西里爾語([gueu])中的字母г具有代碼點值U+0433,其十進制形式是1075。我需要值1075cmap表中進行查找。

// utility function for output 
void hex_print(const std::string& s) 
{ 
    std::cout << std::hex << std::setfill('0'); 
    uint16_t i = 0, dec; 
    for(unsigned char c : s) { 
     std::cout << std::setw(2) << static_cast<int>(c) << ' '; 
     dec = (i++ % 2 == 0) ? (c << 8) : (dec | c); 
     printf("Unicode Value: U+%04x Decimal value of code point: %d\n", codePoint, codePoint); 
    } 
} 

的std :: string被編碼無關。它基本上存儲字節。 std :: wstring很奇怪,雖然也沒有被定義爲保存任何特定的編碼。在Windows中,wchar_t用於UTF-16

是的,我想當你理解「while」時你認爲(至少我做過)字符串只是存儲「ASCII」字符(在此處保留) ,這似乎是錯誤的。事實上,std :: string只是註釋中的字節。雖然很明顯,如果你看一下串english的字節你:

std::string eng = "english"; 
hex_print(eng); 
65 6e 67 6c 69 73 68 

,如果你做「同樣的事情сапоги你:

std::string cyrillic = "сапоги"; 
hex_print(cyrillic); 
d1 81 d0 b0 d0 bf d0 be d0 b3 d0 b8 

我真的很想知道/理解是如何隱式完成這種轉換?爲什麼UTF-8編碼在這裏而不是UTF-16,並且是否有可能改變(或者是由我的IDE或OS定義的)?顯然,當我複製粘貼字符串在我的文本編輯器中,它實際上已經複製了一個12字節的數組(這12個字節可能是utf-8或utf-16)

我認爲Unicode和編碼之間存在混淆。 Codepoint(AFAIK)只是一個字符代碼。 UTF 16給你的代碼,所以你可以說你的0x0441是西里爾小寫字母的情況下的一個代碼點。據我瞭解,UTF16與Unicode代碼點一對一映射,其範圍爲1M和某些字符。但是,其他編碼技術(例如UTF-8)不會直接映射到Unicode代碼點。所以我猜,你最好堅持使用UTF-16

沒錯!我發現這個評論確實非常有用。因爲是的,在編碼Unicode代碼點值的方式與Unicode值本身無關的事實方面存在着混淆(而且我感到困惑),很好,因爲事實上,事情可能會誤導我,因爲我會現在顯示。 You can indeed encode the string сапоги using UTF8 and you will get

d1 81 d0 b0 d0 bf d0 be d0 b3 d0 b8 

所以很明顯它無關確實字形的Unicode值。現在,如果你使用UTF-16編碼相同的字符串你:

04 41 04 30 04 3f 04 3e 04 33 04 38 

其中04和41是真的信с(西里爾[SE])的兩個字節(十六進制形式)。至少在這種情況下,unicode值和其uint16表示形式之間存在直接映射關係。這就是爲什麼(每維基的解釋 [source]):

兩個UTF-16和UCS-2在該範圍內爲單16位代碼的單位,在數值上等於相應的碼點編碼的代碼點。

但是正如有人在評論中提出的那樣,某些代碼點值超出了可以用2個字節定義的值。例如:

1D307 TETRAGRAM FOR FULL CIRCLE(Tai Xuan Jing Symbols

這正是該評論是在暗示:

據我所知,除非你使用代理UTF-16並沒有涵蓋所有的字符對。它意味着原來,當65K是綽綽有餘,但出去的窗口,使之成爲一個非常尷尬的選擇,現在

雖然是完全準確的UTF-16一樣UTF-8 CAN編碼所有字符,儘管它可以使用最多4個字節(因爲您建議如果需要超過2個字節將使用代理對)。

我試圖使用mbrtoc32做一個轉換爲UTF-32,但在Mac上奇怪地缺少cuchar

順便說一句,如果你不知道什麼是surrogate pair是(我沒有)有a nice post about this on the forum

+0

你想使用像'std :: string str = L「сапоги」'? –

+0

我不知道。我的目標是找到組成字符串的每個字符的Unicode值,並將其轉換爲十進制值。 – user18490

+1

這是一個很好的閱讀:http://reedbeta.com/blog/programmers-intro-to-unicode/ – tntxtnt

回答

2

爲了您的目的,查找並打印每個字符的值,您可能需要使用char32_t,因爲它沒有多字節字符串或代理對,只需轉換爲unsigned long即可轉換爲十進制值。我會鏈接到我寫的一個例子,但聽起來好像你想自己解決這個問題。

C++ 14直接支撐類型char8_tchar16_tchar32_t,除了傳統wchar_t有時意味着UCS-32,有時UTF-16LE,有時UTF-16BE,有時不同的東西。它還允許您在運行時存儲字符串,無論您使用前綴u8",u"和前綴以及\uXXXX unicode轉義作爲回退,以任何格式保存源文件。爲了向後兼容,您可以使用十六進制轉義碼將UTF-8編碼在unsigned char的數組中。

因此,您可以以任何您想要的格式存儲數據。您也可以使用所有區域設置都必須支持的方面codecvt<wchar_t,char,mbstate_t>。在<wchar.h><uchar.h>中也有多字節字符串函數。

我強烈建議您將所有新的外部數據存儲在UTF-8中。這包括你的源文件! (令人煩惱的是,一些較舊的軟件仍然不支持它。)在內部使用相同的字符集作爲您的庫也很方便,這些庫在Windows上將爲UTF-16(wchar_t)。如果你需要固定長度的字符,可以保存任何代碼點而沒有特殊情況,那麼char32_t將會很方便。

-1

本來計算機是爲美國市場設計的,並使用Ascii--美國信息交換代碼。這有7位代碼,只是基本的英文字母和一些標點符號,以及用於驅動紙張和打印機終端的低端代碼。 隨着計算機的開發和開始用於語言處理和數字工作一樣多,這變得不足。發生的第一件事是提出了8比特的各種擴展。這可以覆蓋大部分裝飾的歐洲字符(重音符號等),也可以提供一系列基本的圖形用於創建菜單和麪板,但無法實現這兩種效果。仍然沒有辦法代表希臘語之類的非拉丁字符集。因此提出了一個16位代碼,並稱爲Unicode。微軟很早就採用了這個技術,併發明瞭wchar WCHAR(它有各種標識符)來保存國際字符。然而,它出現了16位不足以保存所有字形的常用情況,而Unicode協會也引入了一些與微軟16位代碼集不兼容的問題。

所以Unicode可以是一系列的16位整數。這是wchar字符串。 Ascii文本現在在高字節之間有零個字符,所以你不能將一個寬字符串傳遞給一個函數Expectign Ascii。由於16位幾乎不足夠,所以還生成了32位unicode集。

然而,當你將unicode保存到一個文件時,這就產生了問題,它是32位的16位嗎?它是大端還是小端。因此,在數據開始時提出了一個標誌來解決這個問題。問題在於,內存中的文件內容不再與字符串內容匹配。

C++ std:;字符串是模板化的,所以它可以使用基本字符或其中一種寬類型,幾乎總是在實踐中使用微軟的16位近Unicode編碼。

UTF-8被髮明出來營救。這是一個多字節可變長度編碼,它使用ascii只有7位的事實。所以如果高位被設置,這意味着你在字符中有兩個,三個或四個字節。現在很多字符串都是英文或主要是人類可讀的數字,所以基本上是ascii。這些字符串在Ascii中與UTF-8中的字符串相同,這使得生活變得輕鬆很多。你沒有字節順序約定問題。你確實有這樣的問題,你必須用UTF-8編碼來解碼UTF-8,而不是完全無關緊要的功能,並且記住按照正確的字節數提前你的讀取位置。

UTF-8確實是答案,但其他編碼仍在使用中,您會遇到它們。

+0

感謝您的努力馬爾科姆。不知道爲什麼人們不贊成你的答案。如果有人不喜歡他/她應該忽略的東西。這個功能應該被刪除( – user18490