所以我们都知道 Mac OS 上的文件系统具有使用完全分解的 UTF-8 的古怪功能。如果您调用 POSIX API,例如realpath()
例如,您将从 Mac OS 返回这样一个完全分解的 UTF-8 字符串。当使用像这样的 API 时fopen()
但是,传递预组合的 UTF-8 似乎也可以。
这是一个小演示程序,它尝试打开一个名为ä
。第一次致电fopen()
传递一个预先组合的 UTF-8 字符串,第二个调用传递一个分解的 UTF-8 字符串,令我惊讶的是,两者都有效。我预计只有第二个可以工作,但预组合的 UTF-8 也可以工作。
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp, *fp2;
fp = fopen("\xc3\xa4", "rb"); // ä as precomposed UTF-8
fp2 = fopen("\x61\xcc\x88", "rb"); // ä as decomposed UTF-8
printf("CHECK: %p %p\n", fp, fp2);
if(fp) fclose(fp);
if(fp2) fclose(fp2);
return 0;
}
现在回答我的问题:
这是定义的行为吗?即是否允许将预组合的 UTF-8 传递给 POSIX API,还是应该始终传递分解的 UTF-8?
怎样才能像这样的功能fopen()
甚至知道传递的文件是否包含预组合或分解的 UTF-8?这难道不会导致各种各样的问题吗?由于传递的字符串可以用两种不同的方式解释,因此可能指向两个不同的文件,因此打开了错误的文件?这让我有些困惑。
EDIT
更让人困惑的是,这种奇怪的行为似乎不仅限于文件 I/O。看一下这段代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("\xc3\xa4\n");
printf("\x61\xcc\x88\n");
return 0;
}
Both printf
调用的作用完全相同,即它们都打印字符ä
,第一个调用使用预组合的 UTF-8,第二个调用使用分解的 UTF-8。真的很奇怪。
Unicode 字符串中有两种不同类型的等价形式:一是规范等价,另一个是兼容性。由于您的问题是关于软件似乎认为相同的字符串,所以让我们重点关注规范等价 (OTOH, 兼容性允许语义差异,因此在这个问题中它是题外话)。
引用自Unicode 等效项 https://en.wikipedia.org/wiki/Unicode_equivalence在维基百科中:
代码点序列定义为规范等效是
假定在打印或时具有相同的外观和含义
显示。例如,代码点 U+006E(拉丁文小写字母
“n”)后跟 U+0303(组合波形符“◌̃”)定义为
Unicode 规范地等同于单个代码点 U+00F1
(西班牙语字母表中的小写字母“ñ”)。因此,那些
序列应以相同的方式显示,应以
通过按字母顺序排列名称或搜索等应用程序以相同的方式,
并且可以互相替代.
换句话说,如果两个字符串规范等效,软件应该考虑两个字符串代表完全相同的事物。所以,MacOS 在这里做的是正确的事情:你有两个不同的 UTF-8 字符串(一个是分解的,另一个是预组合的),但它们是规范等效,因此它们映射到同一对象(示例中的文件名相同)。这是正确的(记住“应通过应用程序以相同的方式处理,例如按字母顺序排列名称或搜索,并且可以相互替换”上面引用的行)。
我不太明白你的第二个例子printf()
。是的,分解的字符和预合成的字符都会呈现相同的输出。这正是 Unicode 支持的字符双重表示的要点:您可以选择是使用预先组合的字节序列还是分解的字节序列来表示组合字符。它们打印相同的视觉结果,但表示方式不同。如果两种表示方式都是规范等效(在某些情况下是,在某些情况下不是),那么系统必须将它们视为同一对象的两种表示。
为了在您的软件中更轻松地管理所有这些,您应该标准化您的 Unicode 字符串 https://en.wikipedia.org/wiki/Unicode_equivalence#Normalization在与他们合作之前。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)