Команда «wc -c» и «wc -m» в Linux

24

У меня есть текстовый файл, его содержание:

i k k

Когда я использую wc -mдля подсчета количества символов в этом файле, результат 7 .

Вопрос 1: Но почему я получил 7, не должен ли я получить « 6 », полагая, что он считается символом « конец строки »?

Вопрос 2: Как именно wc -mработает?

Вопрос 3: Когда я использую wc -c(для подсчета байтовых чисел), я получаю тот же результат wc -m, что и какой смысл иметь два разных варианта ? Они делают ту же самую работу, не так ли? Если нет, то какая разница и как wc -cработает?

SWIIWII
источник
1
Вы также могли бы получить 7, если бы ваш файл пришел из Windows с окончаниями строки CRLF
Крис Х

Ответы:

36

Там действительно должно быть всего 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

Ага! Теперь больше байтов, чем символов.

Занна
источник
3
Я использовал команду " cat -A " и наконец нашел, что у меня есть один пробел перед символом конца строки ( $ ). Вот почему я получил 7 вместо 6. Спасибо, " кошка-А " очень помогла.
SWIIWII
2
@SWIIWII Да, я только добавил это к своему ответу, поскольку я думал, что, вероятно, это будет :)
Zanna
1
символ новой строки был также засчитан. Даже если он невидим, он все равно является символом и считается в файле как кусок данных. Хорошее использование кота - кстати. Когда-то тоже можно было использовать hexdump или xxd, чтобы сделать то же самое
Сергей Колодяжный,
@ Да, да, и cat -Aэто тоже покажет. Я добавил к своему ответу, спасибо :)
Zanna
@SWIIWII поместите код в кавычки, `likethis`чтобы сделать его читабельным, не делайте его жирным
phuclv
2
$ locale charmap
UTF-8

В моей нынешней среде набор символов - это UTF-8, то есть символы кодируются от 1 до 4 байтов на символ (хотя, поскольку исходное определение допустимого кода символов UTF-8 указывает на 0x7fffffff, большинство инструментов распознает UTF- 8-байтовые последовательности до 6 байт).

В этом наборе символов доступны все символы из Unicode, например, a aкодируется как значение байта 65, a как 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

В заключение: в более диком слове байт, символ и графем не обязательно совпадают.

Стефан Шазелас
источник
1

Разница между 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
отметка
источник
Или рассмотрим даже UTF-32, где каждый символ имеет 4 байта.
Йорг Миттаг,