[英]How does C uppercase letters?
我在glibc-2.33/ctype/ctype.c
中看到了這段代碼:
// [...]
#define __ctype_toupper \
((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128)
// [...]
int
toupper (int c)
{
return c >= -128 && c < 256 ? __ctype_toupper[c] : c;
}
libc_hidden_def (toupper)
我知道它正在檢查c
是否在 -128 和 256(含)范圍內,如果字符超出該范圍,則按原樣返回,但是_NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128)
是什么意思,我在哪里實際找到源字母如何大寫的代碼? 這似乎是在查找當前的語言環境,我只對en_US.UTF-8
感興趣。 另外,一個角色怎么可能是負面的?
我並不特別關心glibc
,我只想知道 C 中的所有 ASCII 字符(從 NUL 到 DEL 中的所有字符)是如何大寫的。
“C”不將字符轉換為大寫。 C 標准僅要求標准庫中有一個 function 根據當前語言環境正確執行,並且它在“C”語言環境中以特定方式執行(這是保證存在的唯一語言環境)。
庫實現可以按照實現者認為合適的方式自由地完成該任務,並且它們都以不同的方式完成。 甚至完全不同的方式。 一些 C 庫不支持具有 ASCII 字符集的“C”語言環境以外的語言環境。 musl 就是這樣一個C庫的示例,它的實現非常簡單:
int toupper(int c)
{
if (islower(c)) return c & 0x5f;
return c;
}
如您所見,上面的代碼依賴於islower
。 這里是:
int islower(int c)
{
return (unsigned)c-'a' < 26;
}
由於對islower
的調用, toupper
返回小寫字符范圍之外的任何參數不變,即使 arguments 不在 toupper 的有效范圍內。 由於該標准沒有定義有效范圍之外的 arguments 的toupper
行為(基本上可能由fgetc
返回的值),因此僅返回無效的 arguments 不變肯定與任何其他行為一樣可以接受。 Glibc 的toupper
function 經常會在無效的 arguments 上出現段錯誤,因為它使用參數作為數組的索引(如您在引用的代碼中所見)。 根據標准,這種行為也是可以接受的。
Glibc 的實現要復雜得多。 在幕后,它依賴於從語言環境定義文件編譯的語言環境數據,這個過程完全在 C 標准之外,並且在某種程度上由 Posix 標准定義(盡管 GNU 實現在某種程度上與 Posix 不同)。
但這里是獨家新聞:如果您在 UTF-8 語言環境中使用單字節字符,那么 glibc 的復雜代碼都不會產生絲毫差異。 musl 實現完全按照 UTF-8 語言環境的要求工作,因為在單字節 UTF-8 表示中唯一可表示的字母字符是“羅馬”字母表中的 52 個字符。 所有其他 Unicode 字符只能以寬字符和多字節序列表示。
此外,使用 UTF-8 以外的單字節編碼的環境越來越少。 我們當中肯定有很多人不得不學習這些東西,因為我們的程序運行在使用不同ISO-8859-x 代碼頁的各種平台上。 或不同的單字節 Windows 代碼頁。 但最終,Unicode 勝出。 (我們中的許多人都松了一口氣。)因此,除了在遺留環境中,大部分設備不再真正需要。
但這並不是說 Unicode 神奇地解決了管理世界上使用的大量字母表所涉及的所有復雜問題。 離得很遠。 Unicode 所做的有兩個方面:它闡明了復雜性是什么(其中大部分未被 C/Posix 語言環境捕獲),它提供了一些實現的基本標准。
並且,作為副作用,UTF-8 將單字節代碼標准化為基本符合原始 ASCII 7 位標准。 因此,如果您只處理 7 位字符(如今,這可能不太理想),那么除了 musl 風格的實現之外,您不需要任何東西。 如果您正在處理“世界上所有的字符集”,您將尋找一個實際上符合 Unicode 並且使用char
以外的其他東西來表示字符的庫。
但遺憾的是,一個並發症將永遠存在:C 並未標准化char
的簽名這一事實。 在簽署char
的平台上(Unix X86 和 Windows,對於兩個主要示例), (char)0xA0
是 (a) 未指定和 (b) 可能是 -96,這是單字節 0xA0 在 2 的補碼中表示的內容。 因此,如果您編寫的代碼使用ctype.h
中的各種函數並且不處理負char
值,然后您嘗試將該代碼與 UTF-8 編碼字符串一起使用,該字符串包含單字節域之外的字符,然后您最終會將負數傳遞給可能不期望它們的函數。
如果你 go 回到根目錄並尋找_NL_CTYPE_TOUPPER
你會找到一個提交的地方
[..] (ctype_output):支持備用語言環境格式:nelems 變化的計算。 _NL_CTYPE_TOUPPER32 [...]
所以基本上 _NL_CTYPE_TOUPPER 是 _NL_CTYPE_TOUPPER(8bits) 的宏,例如在法語中你有À
作為à
的大寫版本
在此鏈接之后,您將找到 header 文件langinfo.h ,該文件的枚舉從第 43 行開始, _NL_CTYPE_TOUPPER
在第 259 行定義。
LC_CTYPE 類別:字符分類。 256 此信息由 <ctype.h> 中的函數訪問。
LC_CTYPE 是為每種語言定義的,例如法語:
fr_FR:2000"
請注意,調用此 function 並沒有多大意義,因為 ASCII 表中不包含帶重音的字符,但由於此 function 是同時處理 utf8 和 ascii 的字符,因此它是這樣工作的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.