Держите одинаковые слова вместе ...
Для списков слов часто более полезно сгруппировать «одинаковые» слова вместе (даже если они различаются по регистру). Например:
Keeping things together: Simple "M after m":
------------------------ -------------------
mars mars
mars bar mars bar
Mars bar milk
milk milk-duds
Milk milky-way
milk-duds Mars bar
milky-way Milk
Milky-way Milky-way
Если вы хотите, чтобы слова были расположены как в первом столбце, я предлагаю три способа сделать это:
- Использование
strcasecmp()
в сочетании с strcmp()
.
- Однопроходная реализация, отслеживающая тип символа с помощью
isalpha()
, tolower()
и isupper()
.
- Однопроходная реализация, использующая таблицу сортировки.
В конце я обсуждаю две альтернативы:
- Использование таблицы сопоставления для создания произвольного порядка.
- Установка языкового стандарта для использования сортировки на основе языкового стандарта.
Использование доступных библиотечных функций
Если это возможно, не изобретайте велосипед. В этом случае мы можем сделать это, используя функцию POSIX strcasecmp()
, чтобы проверить, равны ли они, с помощью сравнения без учета регистра, и вернувшись к strcmp()
, когда они равны.
int alphaBetize (const char *a, const char *b) {
int r = strcasecmp(a, b);
if (r) return r;
/* if equal ignoring case, use opposite of strcmp() result to get
* lower before upper */
return -strcmp(a, b); /* aka: return strcmp(b, a); */
}
(В некоторых системах функция сравнения без учета регистра называется stricmp()
или _stricmp()
. Если она вам недоступна, ниже представлена реализация.)
#ifdef I_DONT_HAVE_STRCASECMP
int strcasecmp (const char *a, const char *b) {
while (*a && *b) {
if (tolower(*a) != tolower(*b)) {
break;
}
++a;
++b;
}
return tolower(*a) - tolower(*b);
}
#endif
Избегайте двух проходов по струнам
Иногда существующие функции работают недостаточно хорошо, и вам нужно делать что-то еще, чтобы ускорить работу. Следующая функция выполняет сравнение примерно таким же образом за один проход и без использования strcasecmp()
или strcmp()
. Но он рассматривает все небуквенные символы как меньшие, чем буквы.
int alphaBetize (const char *a, const char *b) {
int weight = 0;
do {
if (*a != *b) {
if (!(isalpha(*a) && isalpha(*b))) {
if (isalpha(*a) || isalpha(*b)) {
return isalpha(*a) - isalpha(*b);
}
return *a - *b;
}
if (tolower(*a) != tolower(*b)) {
return tolower(*a) - tolower(*b);
}
/* treat as equal, but mark the weight if not set */
if (weight == 0) {
weight = isupper(*a) - isupper(*b);
}
}
++a;
++b;
} while (*a && *b);
/* if the words compared equal, use the weight as tie breaker */
if (*a == *b) {
return weight;
}
return !*b - !*a;
}
При использовании этого сравнения для сортировки milk
и Milk
будут оставаться рядом друг с другом, даже если список включает milk-duds
.
Использование таблицы сопоставления
Вот способ динамического создания таблицы сортировки из «конфигурации». Он служит для иллюстрации метода сравнения, позволяющего изменить способ сравнения строк.
Вы можете сопоставить, как буквы алфавита сравниваются, с помощью некой простой таблицы, которая описывает относительный порядок букв (или любого символа, кроме байта NUL), который вы хотите иметь:
const char * alphaBetical =
"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
Из этого порядка мы можем создать справочную таблицу, чтобы увидеть, как две буквы должны сравниваться друг с другом. Следующая функция инициализирует таблицу, если это еще не было сделано, в противном случае выполняет поиск в таблице.
int alphaBeta_lookup (int c) {
static int initialized;
static char table[CHAR_MAX+1];
if (!initialized) {
/* leave all non-alphaBeticals in their relative order, but below
alphaBeticals */
int i, j;
for (i = j = 1; i < CHAR_MAX+1; ++i) {
if (strchr(alphaBetical, i)) continue;
table[i] = j++;
}
/* now run through the alphaBeticals */
for (i = 0; alphaBetical[i]; ++i) {
table[(int)alphaBetical[i]] = j++;
}
initialized = 1;
}
/* return the computed ordinal of the provided character */
if (c < 0 || c > CHAR_MAX) return c;
return table[c];
}
С помощью этой справочной таблицы мы теперь можем упростить тело цикла функции сравнения alphaBetize()
:
int alphaBetize (const char *a, const char *b) {
int ax = alphaBeta_lookup(*a);
int bx = alphaBeta_lookup(*b);
int weight = 0;
do {
char al = tolower(*a);
char bl = tolower(*b);
if (ax != bx) {
if (al != bl) {
return alphaBeta_lookup(al) - alphaBeta_lookup(bl);
}
if (weight == 0) {
weight = ax - bx;
}
}
ax = alphaBeta_lookup(*++a);
bx = alphaBeta_lookup(*++b);
} while (ax && bx);
/* if the words compared equal, use the weight as tie breaker */
return (ax != bx) ? !bx - !ax : weight;
}
Можем ли мы сделать вещи проще?
Используя таблицу сопоставления, вы можете создавать множество различных порядков с помощью упрощенной функции сравнения, например:
int simple_collating (const char *a, const char *b) {
while (alphaBeta_lookup(*a) == alphaBeta_lookup(*b)) {
if (*a == '\0') break;
++a, ++b;
}
return alphaBeta_lookup(*a) - alphaBeta_lookup(*b);
}
Используя ту же функцию и изменяя строку alphaBetical
, вы можете добиться почти любого нужного вам порядка (алфавитный, обратный алфавитный, гласные перед согласными и т. Д.). Однако расположение одинаковых слов вместе требует перемежения слов с заглавной буквы и слов в нижнем регистре, и это можно сделать только путем сравнения без учета регистра.
Обратите внимание, что с функцией simple_collating()
и указанной мною alphaBetical
строкой Bacon
будет стоять перед milk
, но Mars
будет идти после milk
и перед Milk
.
Если вы хотите отсортировать по вашему языку.
Если вы хотите использовать последовательность сортировки, которая уже определена для вашего языкового стандарта, вы можете установить языковой стандарт и вызвать функцию сравнения сопоставления:
/*
* To change the collating locale, use (for example):
setlocale(LC_COLLATE, "en.US");
*/
int iso_collating (const char *a, const char *b) {
return strcoll(a, b);
}
Теперь, изменив языковой стандарт, порядок сортировки будет основан на стандартизированной последовательности сортировки.
person
jxh
schedule
23.08.2014
strcmp
помещаетMilk
перед всеми строчными буквами (из-за ASCII), лексикографический порядок группирует слова в одинаковом, но разном регистре вместе. Вопрос ОП касается порядка внутри этих групп. - person Jongware   schedule 23.08.2014milk
vsmIlk
. - person Jongware   schedule 23.08.2014' '
, как и для звукового сигнала'\b'
. Тот факт, что его попросили объединить прописные и строчные буквы и поставить строчные буквы впереди, не означает, что он подразумевал использование полного порядка Unicode, это означает только то, что он есть. - person Utkan Gezer   schedule 27.08.2014