Команда "wc -c" и "wc -m" в Linux
У меня есть текстовый файл, его содержание:
i k k
Когда я использую wc -m
чтобы посчитать количество символов в этом файле, результат равен 7.
Вопрос 1: Но почему я получил 7, не должен ли я получить "6", полагая, что он считается символом "конец строки"?
Вопрос 2: Как именно wc -m
Работа?
Вопрос 3: Когда я использую wc -c
(для подсчета байтов) у меня тот же результат, что и wc -m
Так какой смысл иметь два разных варианта? Они делают ту же самую работу, не так ли? Если нет, то какая разница и как wc -c
работает?
3 ответа
Там действительно должно быть всего 6 символов. Попробуйте запустить
cat -A filename
Чтобы увидеть непечатные символы вашего файла. У тебя должно быть что-то дополнительное. Если я сделаю файл так же, как ваш, я вижу
i k k$
Вы поставили пробел? Это сделало бы 7: i k k $
или, может быть, это новая строка:
i k k$
$
который также 7
Как ты говоришь
wc -m
считает символы и
wc -c
считает байты Если все ваши символы являются частью набора символов ASCII, то для каждого символа будет только 1 байт, поэтому вы получите одинаковое количество от обеих команд.
Попробуйте файл с не ASCII-символами:
$ echo ك > testfile
$ wc -m testfile
2 testfile
$ wc -c testfile
3 testfile
Ага! Теперь больше байтов, чем символов.
$ locale charmap
UTF-8
В моей нынешней среде набор символов - это UTF-8, то есть символы кодируются от 1 до 4 байтов на символ (хотя, поскольку первоначальное определение допустимого кода символов UTF-8 указывает на 0x7fffffff, большинство инструментов распознает UTF- 8-байтовые последовательности до 6 байт).
В этом наборе символов доступны все символы из Юникода, a
кодируется как значение байта 65, 乕
как 3 байта 228 185 149 и é
как двухбайтовая последовательность 195 169, например.
$ printf 乕 | wc -mc
1 3
$ printf a | wc -mc
1 1
Сейчас:
$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15
Я изменил свое окружение, в котором теперь используется набор символов ISO-8859-15 (другие вещи, такие как язык, символ валюты, формат даты, также были изменены, а коллекция этих региональных настроек называется локалью). Мне нужно запустить новый эмулятор терминала в этой среде, чтобы адаптировать рендеринг символов к новой локали.
ISO-8859-15 является однобайтовым набором символов, что означает, что он имеет только 256 символов (фактически даже меньше, чем это фактически покрыто). Этот конкретный набор символов используется для языков Западной Европы, поскольку он охватывает большинство ее языков (и символ евро).
Имеет a
символ со значением байта 65, как в UTF-8 или ASCII, он также имеет é
символ (как обычно используется на французском или испанском, например), но с байтовым значением 233, он не имеет символа 乕.
В этой среде wc -c
а также wc -m
всегда будет давать один и тот же результат.
В Ubuntu, как и в большинстве современных Unix-подобных систем, по умолчанию обычно используется UTF-8, поскольку это единственный поддерживаемый набор символов (и кодировка), который охватывает весь диапазон Unicode.
Существуют другие многобайтовые кодировки символов, но они не так хорошо поддерживаются в Ubuntu, и вы должны пройти через обручи, чтобы иметь возможность сгенерировать локаль с ними, и если вы это сделаете, вы обнаружите, что многие вещи не работать должным образом.
Таким образом, в Ubuntu наборы символов являются однобайтовыми или UTF-8.
Теперь еще несколько заметок:
В UTF-8 не все последовательности байтов образуют допустимые символы. Например, все символы UTF-8, которые не являются символами ASCII, формируются из байтов, для которых все установлены 8-м битами, но где только у первого установлен 7-й бит.
Если у вас есть последовательность байтов с установленным 8-м битом, ни у одного из которых не установлен 7-й бит, то это не может быть преобразовано в символ. И вот тогда у вас начинаются проблемы и несоответствия, так как программное обеспечение не знает, что с ними делать. Например:
$ printf '\200\200\200' | wc -mc
0 3
$ printf '\200\200\200' | grep -q . || echo no
no
wc
а также grep
не найти там персонажа, но:
$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3
bash
находит 3. Когда он не может отобразить последовательность байтов на символ, он считает каждый байт символом.
Это может стать еще более сложным, поскольку в Unicode есть кодовые точки, которые являются недопустимыми в качестве символов, а некоторые не являются символами, и в зависимости от инструмента их кодировка UTF-8 может или не может рассматриваться как символ.
Еще одна вещь, которую следует принять во внимание, - это разница между характером и графическим символом и то, как они отображаются.
$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
3 6
Там мы кодируем 3 символа как 6 байтов, отображаемых как один граф, потому что у нас есть 3 символа, объединенные вместе (один базовый символ, объединяющий острый акцент и объединяющий окружающий круг).
GNU реализация wc
как найдено в Ubuntu есть -L
переключитесь, чтобы сказать вам ширину отображения самой широкой строки на входе:
$ printf 'e\u301\u20dd\n' | wc -L
1
Вы также обнаружите, что некоторые символы занимают 2 ячейки в этом расчете ширины, как наш 乕
персонаж сверху:
$ echo 乕 | wc -L
2
В заключение: в более диком слове байт, символ и графем не обязательно совпадают.
Разница между wc -c
а также wc -m
заключается в том, что в локали с многобайтовыми символами (скажем, UTF8) первый подсчитывает байты, а последний подсчитывает символы. Рассмотрим следующий файл:
$ hexdump -C dummy.txt
00000000 78 79 cf 80 0a |xy...|
(для тех, кто не говорит на UTF8, это буквы 'x', 'y' и 'π', за которыми следует новая строка). Это пять байтов длиной:
$ wc -c dummy.txt
5 dummy.txt
но только четыре символа в длину:
$ wc -m dummy.txt
4 dummy.txt