У меня есть довольно большой (~15 ГБ) набор текстовых файлов. Эти файлы по сути являются простыми базами данных, содержащими учетные данные, и учетные данные в них часто выходят за пределы 128-символьного диапазона ASCII (символы с ударениями и т. п.).
Когда я пытаюсь отсортировать некоторые из этих файлов с помощью:
sort -u input.txt -o output.txt
...Я получаю следующую ошибку:
sort: string comparison failed: Invalid or incomplete multibyte or wide character
sort: Set LC_ALL='C' to work around the problem.
Я много читал о том, как использование LC_ALL=C
может ускорить команды, которые работают с символами, такими как sort
и grep
, включаяБлестящий ответ Стефана Шазеласапо этой теме, но меня особенно беспокоят последствия его использования в моем наборе данных.
Вероятно ли, что запуск LC_ALL=C sort -u
этих файлов приведет к удалению из них каких-либо символов, не входящих в набор ASCII?
Если это, то что я могу сделать вместо этого, чтобы исправить/удалить все "недействительные или неполные многобайтовые или широкие символы" из этих файлов, что позволяет мне сортировать их без использования LC_ALL=C
?
решение1
Приведет ли выполнение команды LC_ALL=C sort -u к удалению из этих файлов любых символов, не входящих в набор ASCII?
В данном случае нет, нет — sort
он будет работать непосредственно со значениями байтов, а не пытаться преобразовать их в символы.
Однако то же самое не обязательно применимо к другим инструментам. Программы, написанные на языке C (язык), с наибольшей вероятностью будут вести себя таким образом. Программы, написанные на языках с сильным различием байтов и символов, например, на Python 3, должны полностью отказаться принимать ввод, который не соответствует набору символов. И я, конечно, могу представить себе плохо написанные программы, которые игнорируют ошибки и выводят � или ?
вместо этого.
Если это так, то что я могу сделать вместо этого, чтобы исправить/удалить все «недопустимые или неполные многобайтовые или широкие символы» из этих файлов, что позволит мне сортировать их без использования LC_ALL=C?
Убедитесь, что все они используют одну и ту же кодировку файла (предпочтительно UTF-8), и что ваша локаль использует ту же кодировку. Ошибка никогда не должна возникать для допустимого файла UTF-8, независимо от его размера.
решение2
Поскольку в итоге мне пришлось пропускать свои файлы через множество различных инструментов Bash, таких как sort
, grep
, awk
, wc
и tr
, я решил, что безопаснее будет воспользоваться "правильным решением", указанным в принятом ответе; сначала преобразовать их все в UTF-8. Это оказалось немного сложнее, чем я ожидал, не в последнюю очередь потому, что мне потребовалось некоторое время, чтобы понять, что file
это ненадежный способ определения того, является ли файл ASCII или UTF-8 (потому что он не проверяет весь файл), поэтому я размещаю этот ответ здесь для потомков.
Чтобы окончательно определить, в какой кодировке находятся ваши файлы, сначала убедитесь, что uchardet
пакет установлен с помощью установщика Cygwin илиapt-cyg, затем запустите:
uchardet *.txt
Или, если вы не используете Cygwin:
chardet *.txt
chardet
Переместите все перечисленные файлы ASCII
в отдельную папку и выполните for
в этой папке следующий цикл:
for i in *.txt; do iconv -f ASCII -t UTF-8 "$i" >> "${i%.txt}_utf.txt"; done;
Он пройдёт по всем .txt
файлам в папке и создаст их версии UTF-8 с utf
добавленным суффиксом.
Повторный запуск uchardet *.txt
может по-прежнему отображать некоторые файлы как ASCII
. Это происходит потому, что ASCII является подмножеством UTF-8, ипросто означаетчто эти файлы не содержат символов, выходящих за пределы диапазона ASCII в 128 бит.
Теперь вы сможете работать sort
без необходимости использования LC_ALL=C
.