История изменений (1 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619848)
Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.
Го Комура (2026). Кодировки текста и переводы строк в Windows — основы искажения текста и CRLF/LF. KomuraSoft LLC. https://comcomponent.com/ru/blog/2026/04/18/000-windows-text-encoding-line-endings/
- DOI (зарегистрированный архив)
- 10.5281/zenodo.21619848
- DOI (последняя зарегистрированная версия)
- 10.5281/zenodo.21619849
В консультациях о работе с текстом в Windows следующие темы очень часто смешиваются сразу все вместе.
- В чём разница между
Shift_JISиUTF-8 - Почему возникает искажение кодировки
- В чём разница между
CRLFиLF - Файл перевели в
UTF-8, но почему он всё равно иногда не читается - Почему один и тот же файл выглядит по-разному в редакторе, консоли, Excel и Git
Дело здесь не в сложности японского языка. Почти всегда причина в том, что одну и ту же последовательность байтов прочитали с другим предположением, либо неверно прочитанное содержимое сохранили как есть.
К тому же в Windows до сих пор сосуществуют мир Unicode и мир кодовых страниц. Добавьте сюда BOM, перевод строки, автоопределение в редакторе, кодовую страницу консоли и преобразование переводов строк в Git — и тема начинает выглядеть запутанной.
В этой статье мы с практической точки зрения разберём часто путаемые в Windows Shift_JIS / UTF-8 / UTF-16, почему возникает искажение кодировки, в чём разница между CRLF и LF и почему эта тема так легко приводит к путанице.
Материал опирается на открытые сведения Microsoft Learn, PowerShell, Git, W3C / Unicode по состоянию на апрель 2026 года. Подробности см. в списке источников в конце статьи.
Для кого эта статья и какие предпосылки
| Пункт | Содержание |
|---|---|
| Читатели | Разработчики и сотрудники ИТ, которые в Windows обмениваются текстовыми файлами (CSV, журналы, конфигурация, исходный код) с другими отделами, другими системами и стороной Linux |
| Предварительные знания | Особых нет. Статья построена так, чтобы её можно было читать, ещё не различая последовательность байтов и кодировку |
| Предполагаемая среда | Windows 10 / Windows 11. По PowerShell затрагиваются и Windows PowerShell 5.1, и PowerShell 7 |
| Что не разбираем | API преобразования кодировки конкретных библиотек, проектирование шрифтов и глифов, нормализацию вроде полной и половинной ширины |
Если нужно быстро: для общей картины достаточно глав 1 и 2, для локализации причины — глав 3 и 8, а если нужно зафиксировать правила эксплуатации — начните с главы 7.
Содержание
- Что важно понять в первую очередь
- Разбираем термины
- 2.1 Чем отличаются Unicode / UTF-8 / UTF-16 / CP932
- 2.2 Как относиться к
Shift_JISиCP932 - 2.3 Ловушки слов
ANSI,UnicodeиUTF-8N - 2.4 Что такое BOM и почему он бывает и у UTF-8
- Почему возникает искажение кодировки
- 3.1 Что на самом деле такое искажение кодировки
- 3.2 Сбой отображения и повреждение данных — разные вещи
- 3.3 Сведение к символам, которых нет в целевой кодировке, необратимо
- В чём разница между символами перевода строки
- 4.1
CRLF/LF/CR - 4.2 Перевод строки — отдельный вопрос от кодировки
- 4.3
\nи байты перевода строки в файле не всегда совпадают
- 4.1
- Почему в Windows особенно легко запутаться
- 5.1 Unicode и устаревшие кодовые страницы сосуществуют
- 5.2 Подписи не согласованы
- 5.3 Если текст только ASCII, проблема остаётся скрытой
- 5.4 Содержимое файла, имя файла, консоль и исходный файл — разные уровни
- 5.5 BOM и перевод строки действуют по отдельной оси
- 5.6 Инструменты меняют содержимое сами
- Типичные сценарии сбоев
- Правила, которые снижают число сбоев на практике
- Расследование искажения кодировки и разницы в окончаниях строк ведём по этим 5 вопросам
- Итог
- Похожие статьи
- Источники
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 19, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
1. Что важно понять в первую очередь
Если сразу перечислить главное, важны следующие семь пунктов.
- Текстовый файл состоит не из самой строки, а из последовательности байтов + кодировки + перевода строки. Иногда к этому добавляется ещё BOM (Byte Order Mark, метка порядка байтов).
- Искажение кодировки возникает, когда одну и ту же последовательность байтов декодируют в другой кодировке.
- Проблемы с переводом строки возникают, когда кодировка верна, но не совпадает предположение о разделителе строк.
UnicodeиUTF-8— не одно и то же.Unicodeотносится к набору символов, аUTF-8иUTF-16— это кодировки, которые превращают его в последовательность байтов.- То, что в Windows называют «
Shift_JIS», на практике безопаснее мыслить как CP932 / японскую кодовую страницу семейства Windows — так разговор меньше расходится. - Одной фразы «перешли на UTF-8» для спецификации недостаточно. Правилом эксплуатации это становится только когда решены ещё наличие BOM и тип перевода строки.
- Источник путаницы — не сам японский язык, а то, что на одной Windows сосуществуют несколько предположений с разной историей.
При работе с текстом в Windows отправная точка — разделить следующие четыре момента.
- Какая последовательность байтов в этом файле
- В какой кодировке его записали
- В какой кодировке его прочитали
- Перевод строки —
CRLFилиLF
Уже одно это разделение заметно снижает вероятность запутаться.
2. Разбираем термины
2.1 Чем отличаются Unicode / UTF-8 / UTF-16 / CP932
Сначала быстрее один раз разобрать слова по отдельности.
| Термин | Что обозначает | Пример | Частая путаница |
|---|---|---|---|
| Unicode | Рамка, в которой символам сопоставляют номера | U+3042 (あ) |
Считают тем же, что UTF-8 |
| UTF-8 | Кодировка, которая переводит Unicode в байты | E3 81 82 |
Считают самим Unicode |
| UTF-16LE | Кодировка, которая переводит Unicode в байты | 42 30 |
Смешивают с пунктом меню Unicode |
| CP932 | Устаревшая японская кодовая страница Windows | 82 A0 |
Считают полностью тем же, что Shift_JIS |
| CRLF / LF | Байты-разделители строк | 0D 0A / 0A |
Считают разновидностью кодировки |
| BOM (Byte Order Mark) | Идентифицирующие байты в начале файла | EF BB BF и т. п. |
Считают названием самой кодировки |
Например, даже для одного символа あ байты различаются в зависимости от кодировки.
Символ: あ
UTF-8 : E3 81 82
CP932 : 82 A0
UTF-16LE : 42 30
Важно понимать: символ и последовательность байтов — разные вещи. На экране приложение работает с тем, что выглядит как «символы», но при сохранении и передаче в конечном счёте обмениваются байтами. Сбои чаще всего происходят именно на границе этого преобразования.
2.2 Как относиться к Shift_JIS и CP932
На местах текстовые файлы японской Windows часто огульно называют «Shift_JIS». Для разговора этого достаточно, но для реальной работы такое обозначение немного небрежно.
Если нужно точнее мыслить об устаревшем японском тексте Windows, безопаснее думать о нём как о CP932 или о японской кодовой странице семейства Windows.
Если отнестись к этому небрежно, начинаются вот такие расхождения в разговоре.
- Сказали «сохрани в
Shift_JIS», а собеседник имел в виду CP932 со стороны Windows - На стороне Linux / macOS файл обработали как
shift_jis, но часть файлов, пришедших из Windows, не воспроизводится корректно - Сказали «сохранить в
ANSI», но какая это кодовая страница, зависело от окружения
Поэтому в спецификациях и заметках расследования безопаснее по возможности писать так.
- Не
Shift_JIS, аCP932 - Не
ANSI, аACP (active code page) / в японском окружении обычно CP932 - Не просто
текст, а конкретно, напримерUTF-8 no BOM, LF
2.3 Ловушки слов ANSI, Unicode и UTF-8N
Вокруг Windows источником путаницы становятся и сами словесные ярлыки.
Особенно сбивают с толку следующие три.
ANSIВстречается в интерфейсе Windows и в старых описаниях, но это не ASCII. Чаще всего имеется в виду активная кодовая страница (ACP) данной машины.UnicodeВ некоторых редакторах и инструментах пункт менюUnicodeна деле означает UTF-16LE. Фраза «сохранил в Unicode» не обязательно означаетUTF-8.UTF-8NВстречается в редакторах японоязычной среды; обычно это лишь ярлык интерфейса для обозначения UTF-8 без BOM, а не официальное название кодировки.
Иными словами, в Windows одно и то же слово у разных инструментов означает разное. Это первая крупная точка путаницы.
2.4 Что такое BOM и почему он бывает и у UTF-8
BOM — это сокращение от Byte Order Mark (метка порядка байтов): знак U+FEFF, который ставят в начало файла или потока. Как следует из имени, исходная роль — показать порядок байтов.
UTF-16 и UTF-32 представляют символ блоками по 2 / 4 байта, поэтому читающей стороне нужно сообщить, укладывать ли блок «с младшего байта» (little-endian) или «со старшего байта» (big-endian). Для этого в начало ставят BOM, и порядок байтов самого BOM показывает, что дальше действует тот же порядок.
| Байты в начале | Значение |
|---|---|
FF FE |
UTF-16 little-endian |
FE FF |
UTF-16 big-endian |
EF BB BF |
UTF-8 |
Здесь возникает вопрос: UTF-8 работает по одному байту и, казалось бы, не имеет проблемы порядка — почему тогда BOM всё равно ставят?
Причина в том, что BOM у UTF-8 используют не как метку порядка байтов, а как подпись (сигнатуру) со смыслом «этот файл — UTF-8». UTF-8 совместим с ASCII, поэтому по содержимому из одних латинских букв и цифр его не отличить от устаревшей кодовой страницы. Если в начале лежит EF BB BF, читающая сторона может решить: «читать не как активную кодовую страницу, а как UTF-8».
Обратная сторона выглядит так.
- Плюс BOM: читающей стороне труднее ошибиться в угадывании кодировки. В частности, Windows PowerShell 5.1 читает файлы скриптов без BOM как активную кодовую страницу, поэтому скрипты с символами вне ASCII без BOM ломаются.
- Минус BOM: в начале появляются лишние 3 байта. Обработчики, которые не знают про BOM, воспринимают это так, будто в начало первой строки попал невидимый символ. Отсюда сбои вроде «имя только в первом столбце заголовка CSV не совпадает» или «строка
#!в shell-скрипте не распознаётся».
То есть BOM у UTF-8 — не вопрос «правильно / неправильно его ставить», а настройка, которую выбирают в зависимости от того, кто будет читать файл. Именно поэтому, как в 7.1, недостаточно «перейти на UTF-8»: нужно ещё решить, with BOM это или no BOM.
3. Почему возникает искажение кодировки
3.1 Что на самом деле такое искажение кодировки
Суть искажения кодировки довольно проста.
- Строку превращают в последовательность байтов в некоторой кодировке
- Эти байты превращают обратно в строку в другой кодировке
- Если предположения не совпадают, получается другая строка
Например, если сохранить あ в UTF-8, байты будут такими.
E3 81 82
Если прочитать их как UTF-8, получится あ, а если исходить из предположения CP932 — они будут выглядеть как другая строка вроде 縺�.
В этом случае повреждено не «японское», а предположение о декодировании.
Если сформулировать искажение кодировки одной строкой, получится так.
Одну и ту же последовательность байтов прочитали как другую кодировку.
flowchart TB
A["Символ あ"] -->|"кодируем в UTF-8 и сохраняем"| B["Байты E3 81 82<br/>в файле лежит только это"]
B -->|"декодируем как UTF-8"| OK["Символ あ ── предположения совпали"]
B -->|"декодируем как CP932"| NG["Другая строка вроде 縺 ── предположения разошлись"]
Рис. 1: На развилке содержимое файла (последовательность байтов) одно и то же. Различается только предположение, которое поставила читающая сторона
3.2 Сбой отображения и повреждение данных — разные вещи
Здесь важно разделять стадию, когда ещё можно вернуть исходное, и стадию, когда вернуть уже трудно.
Например, при следующей последовательности восстановление ещё возможно.
- Файл в UTF-8 открывают как CP932
- На экране это выглядит как
縺� - Файл ещё не сохраняли
На этой стадии исходные байты по-прежнему остаются UTF-8. Если заново открыть файл в правильной кодировке, содержимое часто возвращается.
Опасна следующая последовательность.
- Файл в UTF-8 неверно читают как CP932
- Содержимое, которое выглядит сломанным, сохраняют как есть
- Исходные байты UTF-8 теряются
На этом этапе речь уже идёт не о сбое отображения, а о повреждении данных.
flowchart TD
F["Файл, сохранённый в UTF-8<br/>байты — E3 81 82"]
F -->|"открываем в предположении CP932"| V["На экране видно что-то вроде 縺<br/>= сбой отображения. Байты ещё UTF-8"]
V -->|"открываем заново"| OK["Если указать правильную кодировку, содержимое возвращается"]
V -->|"сохраняем как есть"| BAD["Неверно прочитанную строку записывают обратно в CP932<br/>= повреждение данных. Исходные байты утрачены"]
BAD --> NG["Даже узнав позже правильную кодировку, вернуть уже нельзя"]
Рис. 2: Развилка — ровно один шаг: «открыли заново или сохранили». В расследовании это нужно проверять первым
На практике важно не сводить всё к одной фразе «файл искажён», а как минимум разделять следующие два вопроса.
- Остаются ли сами байты корректными
- Было ли уже пересохранено неверно прочитанное содержимое
3.3 Сведение к символам, которых нет в целевой кодировке, необратимо
Ещё одна опасная ситуация — сведение строки Unicode к узкой кодовой странице вроде CP932.
Если в строке есть символы, которых нет у принимающей стороны, происходит одно из следующего.
- Символ заменяется на
? - Вставляется символ-заменитель
- Возникает ошибка преобразования
- Символ подгоняется под другой, похожий
Например, некоторые эмодзи и расширенные иероглифы напрямую в CP932 не переводятся. Такой сбой нужно оценивать не по критерию «читается или нет», а по тому, возвращается ли исходное значение после прямого и обратного преобразования.
Однажды утраченную информацию не восстановить, даже узнав впоследствии правильную кодировку.
4. В чём разница между символами перевода строки
4.1 CRLF / LF / CR
Перевод строки — это тоже байты.
CR= carriage return =0DLF= line feed =0A- В текстовых файлах Windows традиционно используется
CRLF(0D 0A) - В Linux / Unix обычно используется
LF(0A) - Одиночный
CRиногда встречается в старых контекстах вроде классических Mac
В виде таблицы это выглядит так.
| Перевод строки | Байты | Основной контекст |
|---|---|---|
CRLF |
0D 0A |
Традиционные текстовые файлы Windows, устаревшие инструменты |
LF |
0A |
Linux / macOS / большинство инструментов разработки |
CR |
0D |
Довольно старые устаревшие данные |
4.2 Перевод строки — отдельный вопрос от кодировки
Это довольно важный момент.
Перевод строки — отдельный вопрос от кодировки.
Даже в одном и том же файле UTF-8 перевод строки может быть и CRLF, и LF.
Например, для содержимого «A, перевод строки, B» байты будут различаться так.
UTF-8 + LF : 41 0A 42
UTF-8 + CRLF : 41 0D 0A 42
То есть вполне обычная ситуация, когда:
UTF-8, но отличается только перевод строкиCP932, но перевод строки —LFUTF-16LE, но перевод строки —CRLF
Поэтому когда говорят «перешли на UTF-8, а всё равно не то», на деле иногда расходится не кодировка, а только перевод строки.
4.3 \n и байты перевода строки в файле не всегда совпадают
Именно здесь программисты незаметно для себя чаще всего путаются.
То, что в исходном коде написано \n, ещё не значит, что в файле обязательно окажется только 0A.
В текстовом режиме языка, среды выполнения или I/O API символ \n в Windows иногда преобразуется в CRLF.
То есть может разойтись следующее.
- Представление перевода строки в исходном коде
- Строка во время выполнения
- Байты, сохранённые в файле
- Перевод строки, видимый в редакторе
Из-за этого случаются сбои вроде «я был уверен, что написал LF, но в файле оказался CRLF».
Современные редакторы спокойно работают с одним лишь LF, но у сопутствующих инструментов, устаревших приложений и рабочих процессов предположение о CRLF всё ещё сохраняется.
Поэтому проблема перевода строки — не «дела давно минувших дней»: она и сегодня регулярно встречается на практике.
5. Почему в Windows особенно легко запутаться
5.1 Unicode и устаревшие кодовые страницы сосуществуют
Это главная причина, по которой в Windows всё так запутано.
В Windows сохраняются оба пути:
- обработка через Unicode
- обработка на основе кодовых страниц
Более новые приложения, веб и кроссплатформенные ресурсы тяготеют к UTF-8, тогда как в старых CSV, TXT, журналах, вокруг Excel и в интеграции рабочих систем сохраняется CP932. Кроме того, в части вывода и вокруг некоторых API вполне обычным делом остаётся и UTF-16LE.
Иными словами, на одной машине с Windows сосуществует сразу несколько текстовых культур.
5.2 Подписи не согласованы
Путаницу усиливает не столько сама технология, сколько расхождение ярлыков.
- Говорят
Shift_JIS, а по факту это CP932 - Говорят
ANSI, а по факту это активная кодовая страница - Говорят
Unicode, а по факту это UTF-16LE - Говорят
UTF-8, но наличие или отсутствие BOM не определено - Появляются собственные ярлыки редакторов вроде
UTF-8N
Если оставить это без уточнения, разговор внешне выглядит слаженным, но реальное содержание не совпадает.
5.3 Если текст только ASCII, проблема остаётся скрытой
Это тоже значимый фактор.
Поскольку UTF-8 совместим с диапазоном ASCII, файл, содержащий только латинские буквы, цифры и знаки, иногда «более-менее читается» даже при неверном предположении о кодировке. На стороне CP932 диапазон, эквивалентный ASCII, тоже редко выглядит искажённым внешне, поэтому проблема не всплывает на поверхность.
В результате складывается такая картина.
- Конфигурационный файл только на английском выглядит без проблем
- Стоит добавить одну строку японского текста, как всё ломается
- Проблема, скрывавшаяся всё это время, впервые проявляется уже в эксплуатации
Поэтому сбои кодировки часто выглядят так, будто «вчера всё работало, а сегодня вдруг сломалось». На деле чаще всего оказывается, что мина была заложена давно, а проявилась она лишь в момент появления символов вне ASCII.
5.4 Содержимое файла, имя файла, консоль и исходный файл — разные уровни
В Windows легко запутаться, если объединить всё нижеперечисленное одним словом «кодировка».
- Имя файла / путь
- Содержимое файла
- Отображение в консоли
- Кодировка самого файла исходного кода
- Формат строк во время выполнения
- Отображение в буфере обмена и элементах GUI
Например, даже если японское имя файла отображается нормально, содержимое самого файла может быть сохранено в CP932. И наоборот: даже если сам файл в UTF-8, при несовпадении кодовой страницы консоли исказится только отображение.
Такие операции, как chcp 65001, в основе своей влияют лишь на предположение на стороне консоли и не меняют байты уже существующего файла.
Более того, даже если файл исходного кода в UTF-8, журнал, записываемый во время выполнения, не обязательно окажется тоже в UTF-8. Каждый раз нужно чётко разделять, о кодировке какого именно уровня идёт речь.
Кстати, в японской Windows символ \ иногда отображается как знак иены, и это тоже легко путают с вопросом кодировки.
Однако в большинстве случаев это проблема отображающего шрифта или глифа, а не изменение смысла разделителя пути или экранирующего символа.
5.5 BOM и перевод строки действуют по отдельной оси
Одного лишь слова UTF-8 достаточно только для половины дела.
На практике важны и следующие моменты.
- Есть BOM или нет
CRLFилиLFиспользуется для перевода строки
Например, даже в рамках одного и того же UTF-8:
- есть инструменты Windows, которые читают файл только при наличии BOM
- есть обработка на стороне Unix, где из-за BOM в первом столбце появляются лишние символы
- есть устаревшие инструменты, которым трудно работать с одиночным
LF - при
CRLFмогут портиться shell-скрипты и diff
То есть сбой возможен, даже если кодировка совпадает.
5.6 Инструменты меняют содержимое сами
Ещё сложнее то, что используемые инструменты меняют содержимое неявно, сами по себе.
- Редактор автоматически определяет кодировку
- При сохранении добавляет или убирает BOM
- Git преобразует
CRLF/LF - Оболочка или команда сохраняет файл в кодировке по умолчанию
- Экспорт CSV использует неожиданную кодовую страницу
- В разных версиях PowerShell и других инструментов различаются значения по умолчанию
То есть даже если исполнитель ничего явно не указывал, на практике в Windows какой-то из слоёв сам добавляет своё предположение.
Это и есть истинная причина ситуации «я ничего не менял, а всё сломалось». На деле нередко меняет не человек, а значения по умолчанию инструмента.
6. Типичные сценарии сбоев
Типичные сбои в виде таблицы выглядят так.
| Ситуация | Что на самом деле расходится | Типичный симптом |
|---|---|---|
Устаревший инструмент Windows принимает конфигурационный файл UTF-8 no BOM за ANSI / CP932 |
Предположение о декодировании | Искажается только японский текст |
| CSV в CP932 передают в систему, работающую по предположению UTF-8 | Предположение о декодировании | �, ошибка декодирования, бессмысленный японский текст |
| Журнал в UTF-16LE передают в текстовый инструмент Unix | Предположение о кодировке | Примешиваются NUL-байты, файл выглядит как двоичный |
Файл исходного кода в LF в другом окружении преобразуют в CRLF |
Предположение о переводе строки | Огромный diff по концам строк, сбои в скриптах |
| Неверно прочитанное содержимое сохраняют как есть | Сами байты превращаются в нечто иное | Необратимое повреждение данных |
| В спецификации написано только «выгрузить CSV» | Интерфейс не определён | Excel читает, а другой инструмент ломается |
| Решили лишь «унифицировать на UTF-8» | BOM / перевод строки не определены | Не срабатывают только отдельные инструменты |
Особенно опасен сценарий, при котором увидев сбой отображения, содержимое сохраняют как есть и тем самым окончательно закрепляют сбой.
7. Правила, которые снижают число сбоев на практике
Далее речь пойдёт о том, какие правила эксплуатации стоит определить, чтобы снизить число сбоев.
7.1 Определяем базовый вариант для новых файлов
Для новых файлов разумно в первую очередь выбирать UTF-8. Но одного этого недостаточно.
Как минимум безопаснее определить ещё и следующее.
UTF-8 with BOMилиUTF-8 no BOMCRLFилиLFдля перевода строки- Кто будет читать этот файл
- Нужна ли совместимость с устаревшими инструментами Windows
- Будут ли читать файл также Linux / macOS / CI / контейнеры
Например, для исходного кода и конфигурации, рассчитанных на несколько платформ, первым кандидатом обычно становится UTF-8 no BOM + LF. С другой стороны, если нужно подстроиться под старые инструменты Windows или существующую эксплуатацию, иногда по-прежнему нужны UTF-8 with BOM или CP932 + CRLF.
Важно принимать решение исходя не из общих рассуждений «что правильно», а из того, с кем именно происходит обмен данными.
7.2 Существующие устаревшие файлы не меняем по своей инициативе
Если существующий файл в CP932, безопаснее не переводить его в UTF-8 заодно с повседневной небольшой правкой.
Безопасная схема эксплуатации выглядит так.
- Существующие файлы сохраняют исходную кодировку / BOM / перевод строки
- Преобразование кодировки выделяется в отдельную задачу миграции
- Массовое преобразование выполняется только после проверки объектов конвертации и потребителей ниже по потоку
Сбои с искажением кодировки нередко начинаются с благих намерений — «заодно модернизировать».
7.3 Рассматриваем кодировку и перевод строки как часть интерфейса
Для CSV, TXT, журналов, конфигурационных файлов и простых протоколов интерфейсом является не только содержимое, но и сам формат текста.
В спецификации безопаснее указать как минимум следующее.
- Кодировка
- Наличие BOM
- Тип перевода строки
- Наличие заголовка
- Правила кавычек / разделителя
- Каким инструментом проводилась проверка
Например, одних лишь трёх букв CSV недостаточно.
Только когда написано UTF-8 with BOM, CRLF, разделитель — запятая, с заголовком, разговор перестаёт расходиться.
7.4 На границах чтения и записи указываем явно
На стороне кода тоже безопаснее не полагаться на неявные значения по умолчанию.
- При чтении и записи файла явно указывать кодировку
- Учитывать кодировку и при передаче текста между процессами
- В процедурах экспорта / импорта фиксировать в спецификации и перевод строки
- Не делать небрежное перенаправление оболочки производственным каналом передачи данных
Особенно в Windows «удалось сохранить» и «сохранилось с правильными байтами» — не одно и то же.
7.5 Делимся правилами Git и редактора
Git — не инструмент, который автоматически исправляет кодировку. При этом для перевода строки преобразование иногда всё же происходит.
Поэтому безопаснее определить следующее на уровне репозитория.
- Использовать ли
LFкак основной вариант для исходного кода - Допускать ли
CRLFдля текста, предназначенного только для Windows - Как зафиксировать это в
.gitattributes - Как делиться настройками редактора
Важно рассматривать кодировку и перевод строки отдельно друг от друга. Даже если Git приводит переводы строк к единому виду, сбои с кодировкой никуда не денутся.
7.6 Не останавливаемся на «файл искажён» — говорим, что именно разошлось
На практике хорошо помогает такая замена формулировок.
- Плохая формулировка: «файл искажён»
- Хорошая формулировка: «похоже, файл UTF-8 no BOM открывают с предположением CP932»
- Плохая формулировка: «с концами строк что-то не так»
- Хорошая формулировка: «файл в LF преобразуется в CRLF, из-за чего растёт diff»
Уже одна лишь способность назвать, что именно разошлось, заметно ускоряет расследование.
7.7 Как это проверить — на что смотреть в каждом инструменте
Правила выше начинают работать только в паре со средствами проверки. Для трёх часто используемых инструментов сведём, «где смотреть текущую кодировку» и «где её менять».
| Инструмент | Где смотреть текущую кодировку | Где менять |
|---|---|---|
| VS Code | Строка состояния в правом нижнем углу окна. Рядом стоят кодировка вроде UTF-8 и обозначение перевода строки CRLF / LF |
Щелчок по кодировке в строке состояния открывает варианты открыть заново и сохранить. Значение по умолчанию — в настройках files.encoding |
| Блокнот | В строке состояния отображается кодировка документа | «Файл» → «Сохранить как», поле «Кодировка» в диалоге |
| PowerShell | Надёжнее всего смотреть первые байты файла напрямую (команда ниже) | Параметр -Encoding у записывающего командлета. Значение по умолчанию задаётся через $PSDefaultParameterValues |
VS Code
Кодировка VS Code по умолчанию — UTF-8 (без BOM). Чтобы сменить её для конкретного файла, щёлкните по отображению в строке состояния и выберите: открыть заново уже открытый файл в другой кодировке или сохранить заново в другой кодировке. Если содержимое только неверно прочитали, сначала открывайте заново — это соответствует решению из 3.2.
Чтобы сменить значение по умолчанию, задайте в настройках files.encoding. Значения — utf8 (без BOM), utf8bom (с BOM), utf16le, windows1252 и другие. Можно задавать и отдельно по языкам.
{
"files.encoding": "utf8",
"[powershell]": {
"files.encoding": "utf8bom"
}
}
Разделение из 2.4 — ставить BOM только у скриптов, которые будут работать в Windows PowerShell 5.1, — как раз выражается этой настройкой по языкам.
Блокнот
Начиная с Windows 10 Build 18963 у Блокнота в строке состояния есть столбец с кодировкой документа, а значение по умолчанию для нового файла — UTF-8 (без BOM). При сохранении откройте «Файл» → «Сохранить как» и выберите значение в поле «Кодировка» диалога. В этом поле можно выбрать и наличие BOM, поэтому здесь отражают политику, зафиксированную в 7.1.
Когда сообщают «открыл в Блокноте — текст исказился», быстрее всего сначала проверить это поле и отображение в строке состояния.
PowerShell
Значения по умолчанию в PowerShell сильно зависят от версии. Не зная этого, часто приходят с жалобой «вывел из PowerShell — инструмент не читает».
- PowerShell 6 и новее (линейка PowerShell 7): значение по умолчанию для любого текстового вывода —
utf8NoBOM, то есть UTF-8 без BOM. - Windows PowerShell 5.1: значения по умолчанию не согласованы между командлетами.
Основные случаи в 5.1 в виде таблицы.
| Команда | Значение по умолчанию при записи |
|---|---|
Out-File, операторы перенаправления > >> |
UTF-16LE |
Set-Content, Add-Content (когда назначение пустое или не существует) |
Default (активная кодовая страница; в японском окружении CP932) |
Export-Csv |
Ascii |
Start-Transcript |
UTF-8 with BOM |
New-Item -Type File -Value |
UTF-8 no BOM |
Значения по умолчанию есть и на стороне чтения. Если BOM нет, Get-Content и движок PowerShell при чтении файлов скриптов считают кодировку Default (активная кодовая страница), а Import-Csv и Select-String — UTF-8. Даже для одного и того же файла предположение меняется в зависимости от того, каким командлетом его читают.
Есть ли BOM у файла под руками, видно по первым байтам.
# PowerShell 7: показать первые 3 байта в шестнадцатеричном виде
Get-Content -Path .\sample.txt -AsByteStream -TotalCount 3 | Format-Hex
# В Windows PowerShell 5.1 нет -AsByteStream, поэтому используют -Encoding Byte
Get-Content -Path .\sample.txt -Encoding Byte -TotalCount 3 | Format-Hex
Если в начале EF BB BF — это UTF-8 with BOM, если FF FE — UTF-16LE, иначе BOM нет. Это тот же взгляд, что и в таблице из 2.4.
Чтобы явно зафиксировать значение по умолчанию, используют $PSDefaultParameterValues.
# Задать UTF-8 по умолчанию для всех командлетов с параметром Encoding
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
Здесь есть ловушка между версиями. Одна и та же строка utf8 в Windows PowerShell 5.1 означает UTF-8 with BOM, а начиная с PowerShell 6 — UTF-8 no BOM. Если нужно зафиксировать и наличие BOM, безопаснее явно указать доступные с PowerShell 6 значения utf8BOM / utf8NoBOM.
Кроме того, $OutputEncoding — это кодировка для обмена с внешними программами, и она не влияет на кодировку, с которой перенаправление или командлет сохраняет в файл. Это место легко перепутать.
8. Расследование искажения кодировки и разницы в окончаниях строк ведём по этим 5 вопросам
Если расследование зашло в тупик, быстрее всего вернуться к следующим пяти вопросам.
- Какая последовательность байтов сейчас в этом файле
- Это UTF-8?
- UTF-8 with BOM?
- CP932?
- UTF-16LE?
- Кто и с каким предположением записал файл первым
- Редактор
- Устаревшее приложение
- Экспорт из Excel
- Оболочка / скрипт
- Пакетный процесс / промежуточное ПО
- Кто и с каким предположением читает файл сейчас
- Автоопределение редактора
- Кодовая страница консоли
- Кодировка по умолчанию в библиотеке
- Спецификация на стороне импорта
- Что представляют собой BOM и перевод строки
- BOM есть или нет
CRLF/LF
- Было ли уже сохранено неверно прочитанное содержимое
- Это пока только отображение?
- Или файл уже пересохранён и байты утрачены?
Когда ответы на эти пять вопросов найдены, причина, как правило, становится видна.
9. Итог
Кодировки и переводы строк в Windows выглядят запутанными не потому, что сложен сам японский язык. Причина в том, что последовательность байтов, кодировка, BOM, перевод строки и значения по умолчанию инструментов существуют независимо друг от друга, а вдобавок в Windows сосуществуют старая и новая текстовые культуры.
Особенно стоит запомнить следующие шесть пунктов.
- Искажение кодировки — результат чтения одних и тех же байтов в другой кодировке
- Проблема перевода строки лежит в плоскости, отдельной от кодировки
- Не стоит слишком доверять словам
Shift_JIS,CP932,ANSI,Unicodeбуквально - Одной фразы «перешли на UTF-8» недостаточно, нужны ещё BOM и тип перевода строки
- Сбой отображения и уже сохранённое повреждение данных нужно рассматривать раздельно
- В спецификации писать не просто
текст, а конкретно, напримерUTF-8 no BOM, LF
Иными словами, при работе с текстом в Windows практичнее считать это не «вопросом строк», а вопросом того, как согласовать договорённость о байтах.
10. Похожие статьи
- Введение в кодировки текста в Windows — искажение кодировки (mojibake), возникающее при интеграции с Linux
- Правила промптинга, которые снижают число сбоев кодировки текста у Codex в Windows
11. Источники
- Microsoft Learn, Code Page Identifiers - Win32 apps https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
- Microsoft Learn, about_Character_Encoding - PowerShell https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_character_encoding?view=powershell-7.6
- Microsoft Learn, Understanding file encoding in VS Code and PowerShell https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/vscode/understanding-file-encoding?view=powershell-7.6
- W3C Internationalization, Character encodings: Essential concepts https://www.w3.org/International/articles/definitions-characters/
- Git documentation, gitattributes https://git-scm.com/docs/gitattributes
- Git documentation, git-config https://git-scm.com/docs/git-config
- Microsoft Learn, The Unicode standard - Globalization (байты BOM и то, что BOM у UTF-8 используют как сигнатуру) https://learn.microsoft.com/en-us/globalization/encoding/unicode-standard
- Microsoft Learn, What was new in 20H1 Windows 10 Insider Preview Builds (перевод Блокнота на UTF-8 по умолчанию в Build 18963 и появление отображения кодировки в строке состояния) https://learn.microsoft.com/en-us/previous-versions/windows-insider/archive/new-in-20h1
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Кодировки текста в Windows: кракозябры при обмене с Linux
Почему в Windows появляются кракозябры: на практике разбираем различия CP932, UTF-8, UTF-16, BOM, кодовых страниц, PowerShell и локали Li...
Правила инструкций, которые уменьшают кракозябры Codex в Windows
Практические правила инструкций для Codex в Windows: не сохранять японские файлы наугад, сохранять исходную кодировку существующих файлов...
Порядок разрешения имён в Windows — hosts, кэш DNS, LLMNR/mDNS и DoH
Какой слой ответил — hosts, кэш DNS, DNS-сервер или LLMNR/mDNS — решает, почему часть ПК не подключается. Порядок разрешения имён Windows...
Что на самом деле делает быстрый запуск — почему «Завершение работы» в Windows не то же самое, что перезагрузка
Завершение работы Windows по умолчанию — гибридное: ядро и драйверы сохраняются в hiberfil.sys. Почему только перезагрузка их сбрасывает ...
Декларативное управление конфигурацией Windows через DSC — IaC начинается с dsc.exe
Не пора ли отказаться от сценариев-инструкций, которые ломаются при повторном запуске? Разбираем DSC v3 (dsc.exe): конфигурацию Windows о...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Технические консультации и ревью дизайна
В проектах, где предположения о кодировке CSV, журналов и конфигурационных файлов расходятся между Windows и Linux, заранее зафиксировать контракт ввода-вывода и правила эксплуатации обычно помогает снизить число инцидентов.
Разработка приложений для Windows
В рабочих инструментах для Windows на местах часто сосуществуют CP932 и UTF-8, поэтому заложить работу с кодировкой и переводом строки ещё на этапе проектирования напрямую влияет на сопровождаемость.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Почему возникает искажение кодировки?
- Потому что одну и ту же последовательность байтов прочитали в кодировке, отличной от той, в которой её записали. Например, символ «あ», сохранённый в UTF-8, даёт байты E3 81 82, но при чтении в предположении CP932 выглядит как другая строка вроде «縺». Повреждён не японский текст, а предположение о декодировании. Если же неверно прочитанное содержимое сохранить как есть, исходные байты будут утрачены: это уже не сбой отображения, а повреждение данных. Поэтому важно заново открыть файл в правильной кодировке до сохранения.
- В чём разница между CRLF и LF?
- Это разница в байтах, которыми обозначают границу строки. CRLF — два байта 0D 0A, традиционно используемые в текстовых файлах Windows; LF — один байт 0A, обычный в Linux / macOS и во многих инструментах разработки. Важно, что перевод строки — отдельный вопрос от кодировки. В одном и том же файле UTF-8 перевод строки может быть и CRLF, и LF, поэтому фраза «перешли на UTF-8, а всё равно не то» иногда означает, что разошлась не кодировка, а только тип перевода строки.
- Shift_JIS и CP932 — это одно и то же?
- В разговоре этого обычно достаточно, но на практике безопаснее их различать. Если нужно точно мыслить об устаревшем японском тексте Windows, лучше думать о CP932 или о японской кодовой странице семейства Windows. Точно так же «ANSI» в Windows чаще всего означает активную кодовую страницу данной машины, а пункт меню «Unicode» в редакторе иногда означает UTF-16LE. В спецификациях и заметках расследования стоит писать конкретно, например «UTF-8 no BOM, LF», чтобы разговор меньше расходился.
- Что нужно зафиксировать в спецификации текстового файла?
- Одной фразы «перешли на UTF-8» недостаточно: правилом эксплуатации это становится только когда решены ещё наличие BOM и тип перевода строки. Для файлов обмена вроде CSV и журналов безопаснее прописать кодировку, наличие BOM, перевод строки, наличие заголовка и правила разделителя. Для исходного кода и конфигурации, рассчитанных на несколько платформ, первым кандидатом обычно становится UTF-8 no BOM + LF, а если нужно подстроиться под старые инструменты Windows, иногда по-прежнему нужны UTF-8 with BOM или CP932 + CRLF.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.