簡體   English   中英

C 大寫字母怎么辦?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM