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

时间:2016-10-07 19:23:16

标签: c language-lawyer

根据标准:

  

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

进一步在标准中:

  

...上述小数位数列表中0之后的每个字符的值应大于前一个值。
  (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'应该为真的惯例。但是,除非使用的唯一字符集依赖于此约定,否则这不是真正可移植的;这可能是真的。

这个问题源于另一个问题主题:Check if a letter is before or after another letter in C。在这里,一些人建议你可以使用不等式来确定char中存储的两个字母的顺序。但是一位评论者指出,标准并不保证这种行为。

5 个答案:

答案 0 :(得分:10)

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

答案 1 :(得分:6)

历史上使用的代码不是简单地命令字母表。例如,Baudot将元音放在辅音之前,所以&#39; A&#39; &LT; &#39; B&#39;,但&#39; U&#39; &LT; &#39; B&#39;同样。

还有像EBCDIC这样的代码是有序的,但有差距。所以在EBCDIC中,我是&#39; &LT; &#39; J&#39;,但是&#39;我&#39; + 1!=&#39; J&#39;。

答案 2 :(得分:6)

你可能只是为ASCII字符数字的标准保证字符制作一个表格。如,

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

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

正如你所说,

char c1;
char c2;

通过检查

可以按字母顺序验证
(c1 < sizeof(mytable) && c2 < sizeof(mytable) ? mytable[c1] < mytable[c2] : 0)

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

答案 3 :(得分:4)

对于A-Z,a-z,不区分大小写(并使用复合文字):

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

对于2 char已知为A-Z,a-z但可能是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映射到其排名的查找表。当然,该表与平台有关。

答案 4 :(得分:2)

使用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', "A-Z order unexpected");
  _Static_assert('B' < 'C', "A-Z order unexpected");
  _Static_assert('C' < 'D', "A-Z order unexpected");
  // Other 21  _Static_assert() omitted for brevity
  _Static_assert('Y' < 'Z', "A-Z order unexpected");


  _Static_assert('a' < 'b', "a-z order unexpected");
  _Static_assert('b' < 'c', "a-z order unexpected");
  _Static_assert('c' < 'd', "a-z order unexpected");
  // Other 21  _Static_assert() omitted for brevity
  _Static_assert('y' < 'z', "a-z 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);
}