是否有一种简单,可移植的方法来确定C中两个字符的排序?

根据标准:

执行字符集的成员值是实现定义的。
(ISO / IEC 9899:1999 5.2.1 / 1)

进一步在标准中:

…在上面的十进制数字列表中, 0之后的每个字符的值应该比前一个值大1。
(ISO / IEC 9899:1999 5.2.1 / 3)

看来该标准要求执行字符集包括拉丁字母表的26个大写字母和26个小写字母,但我认为不要求以任何方式对这些字符进行排序。 我只看到十进制数字的订单规定。

这似乎意味着,严格地说,不能保证'a' < 'b' 。 现在,字母表中的字母按顺序分为ASCII,UTF-8和EBCDIC。 但是对于ASCII和UTF-8,我们有'A' < 'a' ,而对于EBCDIC,我们有'a' < 'A'

ctype.h中有一个可以移植地比较字母字符的函数可能会很好。 除了这个或类似的东西,在我看来,必须在语言环境中查找CODESET的值并相应地继续,但这似乎并不简单。

我的直觉告诉我,这几乎不是问题; 对于大多数情况,可以通过转换为小写来处理字母字符,因为对于最常用的字符集,字母是有序的。

问题:给出两个字符

 char c1; char c2; 

是否有一种简单,可移植的方法来确定c1是否按字母顺序排在c2之前? 或者我们是否假设小写和大写字符总是按顺序出现,即使标准似乎不能保证这一点?

为了澄清任何混淆,我真的只对拉丁字母的52个字母感兴趣,这些字母由标准保证在执行字符集中。 我意识到其他字母组很重要,但似乎我们甚至无法知道这一小部分字母的排序。

编辑

我想我需要澄清一点。 我认为,问题在于我们通常认为拉丁字母的26个小写字母是有序的。 我希望能够断言“a”出现在’b’之前,当我们给出’a’和’b’整数值时,我们有一种方便的方式在代码中将其表示为'a' < 'b' 。 但该标准不保证上述代码将按预期进行评估。 为什么不? 该标准确实保证了数字0-9的这种行为,这似乎是明智的。 如果我想确定一个字母字符是否在另一个字母之前,比如用于排序目的,并且如果我希望这个代码真正可移植,那么标准似乎没有帮助。 现在我必须依赖ASCII,UTF-8,EBCDIC等采用的惯例'a' < 'b'应该是真的。 但是,除非使用的唯一字符集依赖于此约定,否则这不是真正可移植的; 这可能是真的。

这个问题起源于另一个问题主题: 检查一封信是在C之前或之后的另一个字母 。 在这里,一些人建议您可以使用不等式确定存储在char的两个字母的顺序。 但是一位评论者指出,标准并不能保证这种行为。

对于AZ,az不区分大小写(并使用复合文字):

 char ch = foo(); az_rank = strtol((char []){ch, 0}, NULL, 36); 

对于已知为AZ的2个char ,az但可以是ASCII或EBCDIC。

 int compare2alpha(char c1, char c2) { int mask = 'A' ^ 'a'; // Only 1 bit is different between upper/lower return (c1 | mask) - (c2 | mask); } 

或者,如果限制为256个不同的char ,则可以使用将char映射到其排名的查找表。 当然,该表与平台有关。

strcoll就是为此目的而设计的。 只需设置两个字符串,每个字符串一个字符。 (通常你想比较字符串,而不是字符)。

历史上使用的代码不是简单地命令字母表。 例如,Baudot将元音放在辅音之前,所以’A’<'B',但'U'<'B'也是如此。

还有像EBCDIC这样的代码是有序的,但有差距。 所以在EBCDIC中,’I’<'J',但'I'+ 1!='J'。

您可能只需要为ASCII字符编号的标准保证字符制作一个表格。 例如,

 #include  static char mytable[] = { ['a'] = 0x61, ['b'] = 0x62, // ... ['A'] = 0x41, ['B'] = 0x42, // ... }; 

编译器会将当前字符集中的每个字符(可能是任何疯狂的字符集)映射到ASCII码,并且不保证存在的字符将映射到零。 然后您可以在需要时使用此表进行排序。

如你所说,

 char c1; char c2; 

可以通过检查来按照字母顺序进行validation

 (c1 < sizeof(mytable) && c2 < sizeof(mytable) ? mytable[c1] < mytable[c2] : 0) 

我实际上在一个研究项目中使用了这个项目,该项目运行在ASCII和EBCDIC上以实现可预测的排序,但它足够便携,可以处理任何字符集。 编辑 :我实际上让表的大小为空,因此它会计算到所需的最小值,因为DeathStation 9000上一个字节可能有32位,因此CHAR_MAX最多可达4294967295或更高。

使用C11,代码可以使用_Static_assert()编译时确保字符具有所需的顺序。

这种方法的一个优点是,由于压倒性的字符编码都准备好满足所需的AZ要求,如果新颖或深奥的平台使用不同的东西,它可能需要不可预见的编码或定制。 在这种情况下,这个最佳代码可以做到无法编译。

使用示例

 // Sample case insensitive string sort routine that insures // 1) 'A' < 'B' < 'C' < ... < 'Z' // 2) 'a' < 'b' < 'c' < ... < 'z' int compare_string_case_insensitive(const void *a, const void *b) { _Static_assert('A' < 'B', "AZ order unexpected"); _Static_assert('B' < 'C', "AZ order unexpected"); _Static_assert('C' < 'D', "AZ order unexpected"); // Other 21 _Static_assert() omitted for brevity _Static_assert('Y' < 'Z', "AZ order unexpected"); _Static_assert('a' < 'b', "az order unexpected"); _Static_assert('b' < 'c', "az order unexpected"); _Static_assert('c' < 'd', "az order unexpected"); // Other 21 _Static_assert() omitted for brevity _Static_assert('y' < 'z', "az order unexpected"); const char *sa = (const char *)a; const char *sb = (const char *)b; int cha, chb; do { cha = toupper((unsigned char) *sa++); chb = toupper((unsigned char) *sb++); } while (cha && cha == chb); return (cha > chb) - (cha < chb); }