UNIX сортировка игнорирует пробелы - PullRequest
15 голосов
/ 03 августа 2011

Данный файл txt:

ab
a c
a a

При звонке sort txt я получаю:

a a
ab
a c

Другими словами, это неправильная сортировка, она как бы удаляет / игнорирует пробелы! Я ожидал, что это будет поведение sort -i, но это происходит с или без флага -i.

Я бы хотел получить "правильную" сортировку:

a a
a c
ab

Как мне это сделать?

Ответы [ 7 ]

16 голосов
/ 03 августа 2011

Решено:

export LC_ALL=C

Из документации sort() :

ПРЕДУПРЕЖДЕНИЕ. Локаль, указанная средой, влияет на порядок сортировки.Установите LC_ALL = C, чтобы получить традиционный порядок сортировки, который использует собственные значения байтов.

(работает как минимум для ASCII, без понятия для UTF8)

11 голосов
/ 03 августа 2011

Как упоминалось ранее, LC_ALL=C sort делает свое дело.Это просто потому, что разные языки имеют разные правила сортировки символов, которые часто выкладываются старшими лингвистами, а не экспертами по CS.И эти правила, в случае с вашей локалью, похоже, говорят, что при сортировке пробелы следует игнорировать.

За счет префикса LC_ALL = C (или, когда LC_ALL не установлен, достаточно LC_COLLATE=C), вы явнообъявите не зависящую от языка сортировку (и, с LC_ALL, форматирование чисел и прочее), что вам нужно в этом контексте.Если вы хотите сделать это по умолчанию, экспортируйте LC_COLLATE в вашу среду.

По умолчанию выбирается таким образом, чтобы сохранить согласованность с "нормальными", реальными схемами сортировки (такими как белые страницы), которыечасто игнорируемые пробелы.

2 голосов
/ 14 апреля 2014

Использование языка C, то есть сортировка только по байтовым значениям, не является хорошим решением для языков, где некоторые буквы находятся за пределами диапазона [A-Za-z]. Такие буквы представлены в UTF-8 в виде нескольких байтов, и тогда порядок упорядочения байтовых значений не соответствует желаемому. (Некоторые символы могут иметь два эквивалентных представления (предварительно составленные и не составленные)).

Тем не менее, обработка пробелов является проблемой. Я попробовал следующее:

$ cat stest  
a b  
a c  
ab  
a d  

$ sort stest  
ab  
a b  
a c  
a d  

$ sort -k 1,1 stest  
a b  
a c  
a d  
ab  

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

2 голосов
/ 28 марта 2012

Вы можете использовать программу 'env', чтобы временно изменить LC_COLLATE на время сортировки;например,

/ usr / bin / env LC_COLLATE = POSIX / bin / sort file1 file2

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

1 голос
/ 19 июня 2013

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

Большинство параметров, которые я видел в Интернете и SO, похоже, рекомендуют то, что я вижу здесь, устанавливая локаль глобально (излишне)

export LC_ALL=C

или добавьте его в каждую отдельную команду следующим образом: gnu.org (утомительно)

$ echo abcdefghijklmnopqrstuvwxyz | LC_ALL=C /usr/xpg4/bin/tr 'a-z' 'A-Z' ABCDEFGHIJKLMNOPQRSTUVWXYZ

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

Мне пришлось по какой-то причине установить LANG вместо LC_ALL, но были установлены все отдельные локали, что достаточно для меня функционально.

Вот тест, простой, как может быть

#!/bin/bash
# locale_checker.sh

#Check and set locale to LC_ALL to optimize character sort and search.
echo "locale was $LANG"
LANG=C
locale

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

mateor@:~/snippets$ ./locale_checker.sh
locale was en_US.UTF-8
LANG=C
LANGUAGE=en_US:en
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_PAPER="C"
LC_NAME="C"
LC_ADDRESS="C"
LC_TELEPHONE="C"
LC_MEASUREMENT="C"
LC_IDENTIFICATION="C"
LC_ALL=
mateor@:~/snippets$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

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

0 голосов
/ 03 августа 2011

Странно, работает здесь (cygwin).

Попробуйте sort -d txt.

0 голосов
/ 03 августа 2011

На самом деле для меня

$ cat txt
ab
a c
a a
$ sort txt
a a
a c
ab

Бьюсь об заклад, между вашим a и c у вас есть неразрывный пробел, либо пробел, либо пробел, либо другой пробел с высоким кодом!

EDIT

Просто запустил его в Linux.Я должен был посмотреть на теги.Да, я получаю тот же результат, что и вы!Мой первый запуск был на Mac.Похоже, разница между GNU и BSD.Я буду исследовать дальше.

РЕДАКТИРОВАТЬ 2:

Linux использует сортировку на основе полей ... все еще ищет способы ее подавления.Попытка

sort -t, txt

в надежде обмануть GNU, заставив думать, что вся строка была одним полем, но она все еще использовала текущую локаль для сортировки.

РЕДАКТИРОВАТЬ 3:

ОПрешил проблему, установив языковой стандарт C с помощью

export LC_ALL=C

Похоже, другого подхода нет.Команда sort будет использовать текущую локаль, и хотя она часто говорит, что C (или ее псевдоним POSIX) является локалью по умолчанию, но если у вас Linux, она, вероятно, была установлена ​​для вас.Введите locale -a, чтобы увидеть доступные локали.В моей системе:

$ locale -a
C
POSIX
en_AG
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IN
en_NG
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZW.utf8

Кажется, что установка языкового стандарта на C (или его псевдоним POSIX) - единственный способ нарушить поведение sort на основе полей и рассматривать всю строку как одно поле.,ИМХО довольно странно, что это как это сделать.Я думаю, что варианты -t или -k, или, возможно, какой-то новый вариант был бы более разумным способом сделать это.

Кстати, похоже, этот вопрос уже задавался ранее на SO: неожиданный результат от сортировки gnu .

...