7

出于我完全不同意的原因,尽管我反对,但“(反可用性)权力”继续颁布法令,我有一个排序例程,它执行基本的 strcmp() 比较以按其名称排序。效果很好;很难弄错那个。但是,在第 11 小时,已决定以数字开头的条目应位于以字母开头的条目之后,这与 ASCII 顺序相反。他们引用 EBCDIC 标准在字母后有数字,因此先前的假设不是普遍真理,我无权赢得这个论点……但我离题了。

这就是我的问题。我已经用一个名为 nonstd_strcmp 的新函数替换了对 strcmp 的所有适当引用,现在需要实现修改以完成排序更改。我使用 FreeBSD 源作为我的基础: http: //freebsd.active-venture.com/FreeBSD-srctree/newsrc/libkern/strncmp.c.html

 if (n == 0)
  return (0);
 do {
  if (*s1 != *s2++)
   return (*(const unsigned char *)s1 -
    *(const unsigned char *)(s2 - 1));
  if (*s1++ == 0)
   break;
 } while (--n != 0);
 return (0);

我想我可能需要花一些时间来真正考虑应该如何完成它,但我敢肯定,我不是唯一一个经历过发布前规范更改的脑死亡的人。

4

6 回答 6

16

您需要做的是为每个字符创建一个排序表。这也是进行不区分大小写比较的最简单方法。

if (order_table[*s1] != order_table[*s2++])

请注意,字符可能已签名,在这种情况下,您的表的索引可能会变为负数。此代码仅适用于签名字符:

int raw_order_table[256];
int * order_table = raw_order_table + 128;
for (int i = -128;  i < 128;  ++i)
    order_table[i] = (i >= '0' && i <= '9') ? i + 256 : toupper(i);
于 2010-06-15T21:30:23.977 回答
8

如果您的权力与我遇到的所有其他权力一样,您可能希望将其设为一个选项(即使它是隐藏的):

排序:

o 字母后的数字

o 数字后的字母

或者更糟糕的是,他们可能会发现他们希望数字按数字排序(例如“A123”“A15”之后),那么它可以是

o 字母后的数字

o 数字后的字母

o 字母后的智能数字

o 智能数字后面的字母

这可以诊断真正的问题,而不是症状。我敢打赌,他们可能会在第 11 小时和第 59 分钟改变主意。

于 2010-06-15T22:12:51.293 回答
5

比较字符时,您可以使用查找表将 ASCII 转换为 EBCDIC ;-)

于 2010-06-15T21:20:31.733 回答
4

在这种只有大写字母(如 OP 在评论中提到的)和数字 0-9 的特殊情况下,您也可以省略顺序表,而是将两个不同的字符乘以 4 并比较结果以 256 为模。ASCII 数字的范围(48 到 57) 不会溢出 8 位 (57 × 4 = 228),但大写字母 (65 到 90) 的范围会 (65 × 4 = 260)。当我们比较乘以 256 的值时,每个字母的值将小于任何数字的值:90×4 % 256 = 104 < 192 = 48×4

代码可能类似于:

int my_strcmp (const char *s1, const char *s2) {
    for (; *s1 == *s2 && *s1; ++s1, ++s2);
    return (((*(const unsigned char *)s1) * 4) & 0xFF) - \
           (((*(const unsigned char *)s2) * 4) & 0xFF);
}

Of course, the order table solution is far more versatile in general as it allows one to define a sort order for every character—this solution is sensible only for this special case with uppercase letters vs digits. (But e.g. on microcontroller platforms, saving even the small amount of memory used by the table can be a real benefit.)

于 2010-06-16T01:22:43.253 回答
3

虽然与上述答案大体一致,但我认为为循环的每次迭代进行查找是愚蠢的,除非您认为大多数比较将具有不同的第一个字符,而您可以这样做

char c1, c2;
while((c1 = *(s1++)) == (c2 = *(s2++)) && c1 != '\0');
return order_table[c1] - order_table[c2];

另外,我建议使用静态初始化器构造 order_table,这将提高速度(无需每次都生成),也可能提高可读性

于 2010-06-15T22:31:07.117 回答
2

这应该是字符串比较的一个很好的实现,类似于其他帖子描述的实现。

static const unsigned char char_remap_table[256] = /* values */

#define char_remap(c) (char_remap_table[(unsigned char) c])

int nonstd_strcmp(const char * restrict A, const char * restrict B) {
     while (1) {
          char a = *A++;
          char b = *B++;
          int x = char_remap(a) - char_remap(b);
          if (x) {
               return x;
          }
          /* Still using null termination, so test that from the original char,
           * but if \0 maps to \0 or you want to use a different end of string
           * then you could use the remapped version, which would probably work
           * a little better b/c the compiler wouldn't have to keep the original
           * var a around. */
          if (!a) { /* You already know b == a here, so only one test is needed */
               return x;  /* x is already 0 and returning it allows the compiler to
                           * store it in the register that it would store function
                           * return values in without doing any extra moves. */
          }
     }
}

除此之外,您可以概括该函数以将 char_remap_table 作为参数,这将使您以后可以根据需要轻松使用不同的映射。

int nonstd_strcmp(const char * restrict a, const char * restrict b, const char * restrict map);
于 2010-06-15T22:16:56.567 回答