strcmp () но с 0-9 ПОСЛЕ AZ?(C / C ++) - PullRequest
7 голосов
/ 16 июня 2010

По причинам, с которыми я полностью не согласен, но «Полномочия (анти-юзабилити), которые будут» продолжают утверждаться, несмотря на мои возражения, у меня есть процедура сортировки, в которой базовая функция strcmp () сравнивается для сортировки по ее имени.Это прекрасно работает;трудно ошибиться.Однако в 11-й час было решено, что записи, начинающиеся с цифры, должны следовать после записей, начинающихся с буквы, вопреки порядку ASCII.Они ссылаются на то, что в стандарте EBCDIC цифры следуют за буквами, поэтому предварительное предположение не является универсальной правдой, и я не имею возможности выиграть этот аргумент ... но я отвлекся.

В этом и заключается моя проблема.Я заменил все соответствующие ссылки на strcmp новым вызовом функции nonstd_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);

Думаю, явозможно, потребуется некоторое время, чтобы по-настоящему подумать о том, как это сделать, но я уверен, что я не единственный, кто испытал умственную смерть от изменений спецификации перед выпуском.

Ответы [ 6 ]

16 голосов
/ 16 июня 2010

Что вам нужно сделать, это создать таблицу заказов для каждого персонажа.Это также самый простой способ проводить сравнения без учета регистра.

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);
8 голосов
/ 16 июня 2010

Если ваши полномочия такие же, как и у всех других, с которыми я сталкивался, вы можете сделать это (даже если они скрыты):

Порядок сортировки:

o Числа после букв

o Буквы после цифр

или, что еще хуже, они могут выяснить, что они хотят, чтобы числа были отсортированы численно (например, "A123" следует после"A15"), тогда это может быть

o цифры после букв

o Буквы после цифр

o Умные числа после букв

o Буквы после умных чисел

Это касается диагностики реальной проблемы, а не симптома. Могу поспорить, что есть небольшая вероятность, что они могут изменить свое мнение на 11-й и 59-й минутах.

5 голосов
/ 16 июня 2010

Вы можете использовать таблицу поиска для перевода ASCII в EBCDIC при сравнении символов; -)

4 голосов
/ 16 июня 2010

В этом особом случае с только заглавными буквами (как указано в комментариях к 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 </p>

Код может выглядеть примерно так:

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);
}

Конечно, решение таблицы заказов в целом гораздо более универсально, поскольку позволяет определять порядок сортировки для каждого символа - это решение целесообразно только для этого особого случая с заглавными буквами * 1009.* буквы против цифр.(Но, например, на платформах микроконтроллеров, экономия даже небольшого объема памяти, используемой таблицей, может быть реальным преимуществом.)

3 голосов
/ 16 июня 2010

Хотя в общем согласии с приведенными выше ответами, я думаю, что глупо выполнять поиск для каждой итерации цикла, если только вы не думаете, что большинство сравнений будут иметь разные первые символы, когда вместо этого вы могли бы сделать

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

Кроме того, я бы порекомендовал создать таблицу order_table со статическим инициализатором, который улучшит скорость (не нужно генерировать каждый раз - или когда-либо), а также, возможно, читабельность

2 голосов
/ 16 июня 2010

Вот то, что должно быть довольно хорошей реализацией сравнения строк, аналогичного описанному в других постах.

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);
...