Команди “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працює?



1
Ви також можете отримати 7, якщо файл прийшов із Windows із закінченнями рядка CRLF
Chris H

Відповіді:


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 " і нарешті виявив, що у мене є один пробіл перед символом " end-of-line " ( $ ). Ось чому я отримав 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, це букви «х», «у» та «π», а потім новий рядок). Вона становить п'ять байтів:

$ wc -c dummy.txt 
5 dummy.txt

але всього чотири символи:

$ wc -m dummy.txt 
4 dummy.txt

Або розглянемо навіть UTF-32, де кожен символ має 4 байти.
Йорг W Міттаг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.