История изменений (2 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Исправлена ошибка отображения: строки с вертикальной чертой выводились как таблица, из-за чего справочные ссылки нельзя было нажать. Текст статьи не изменился.
- Первая публикация
Цитирование статьи(DOI: 10.5281/zenodo.21619770)
Статья заархивирована на Zenodo. Ниже приведены DOI, который всегда ведёт к последней версии, и DOI, закреплённый за версией, которую вы читаете.
Го Комура (2026). Кодировки текста в Windows: кракозябры при обмене с Linux. KomuraSoft LLC. https://doi.org/10.5281/zenodo.21619770 https://comcomponent.com/ru/blog/2026/03/21/000-windows-text-encoding-mojibake-linux/
- DOI (последняя версия)
- 10.5281/zenodo.21619770
- DOI (эта версия)
- 10.5281/zenodo.21619771
Кракозябры (mojibake) в Windows появляются не потому, что японский язык сложен. Почти всегда причина в том, что одну и ту же последовательность байт прочитали как другую кодировку или результат неверного чтения сохранили уже в другой кодировке.
Особенно это видно на стыке Windows и Linux: в Windows до сих пор живут сразу несколько контекстов — CP932, UTF-8, UTF-16, кодовая страница консоли, различия версий PowerShell, — а Linux чаще исходит из UTF-8. Расхождения, которые раньше не были видны, всплывают сразу все.
flowchart TB
accTitle: Как расходятся предположения Windows и Linux
accDescr: В Windows остаются CP932, UTF-16, кодовая страница консоли и различия версий PowerShell, а Linux чаще исходит из UTF-8, поэтому при сочетании двух сред расхождение предположений всплывает наружу.
w1["Windows"] --> w2["CP932 / UTF-16 / кодовая страница / версии"]
l1["Linux"] --> l2["Сильное предположение UTF-8"]
w2 --> z1["Расхождение предположений всплывает"]
l2 --> z1
Рис. 1: Несколько контекстов, оставшихся в Windows, встречаются с предположением UTF-8 в Linux — и расхождение сразу всплывает.
Это не столько про сложность обработки японского текста, сколько про то, удалось ли согласовать, с каким предположением обрабатываются байты. В статье разберём кодировки Windows с точки зрения «почему появляются кракозябры» и по делу соберём места, где при связке с Linux инцидентов становится больше.
Статья для тех, кто передаёт созданные в Windows CSV, журналы и файлы настроек в Linux — или наоборот — и хочет сам разобрать причину кракозябр и восстановить файл. Знание конкретного языка или фреймворка не требуется. В примерах команд используются iconv и PowerShell.
1. Что нужно понять в первую очередь
Если сразу выписать суть, важны следующие шесть пунктов.
- Кракозябры — это не проблема «символов», а проблема «как интерпретировали последовательность байт».
- В Windows сосуществуют мир Unicode и мир устаревших кодовых страниц, и даже на одной машине предположения различаются в зависимости от контекста.
- В Linux предположение UTF-8 сильное, поэтому смесь с CP932 или UTF-16 из Windows легко даёт инцидент.
- Стадию, где сломалось только отображение, и стадию, где уже сохранили испорченное содержимое, нужно рассматривать раздельно.
- Безопаснее для нового текста брать UTF-8 первым вариантом, а существующие legacy-файлы оставлять как есть до явной задачи миграции.
- Кодировка файла, кодировка редактора, кодовая страница консоли и внутренний формат строк приложения — разные вещи. Если их смешать, расследование теряется.
Фразы «в Windows появились кракозябры» недостаточно, чтобы назвать причину. Как минимум нужно отделить, что именно из перечисленного разошлось.
- Кодировка самого файла
- Кодировка в момент сохранения
- Интерпретация редактора
- Входная и выходная кодовые страницы консоли
- Внутренний формат строк приложения
- Локаль Linux и предполагаемая кодировка
flowchart TB
accTitle: Слои, которые разделяют при диагностике кракозябр
accDescr: Одной фразы «в Windows появились кракозябры» причину не найти: нужно отделить, разошлись ли кодировка самого файла, кодировка при сохранении, интерпретация редактора, кодовая страница консоли, внутренний формат приложения или локаль Linux.
s0["В Windows появились кракозябры"] --> q1{"Что разошлось?"}
q1 --> a1["Кодировка самого файла"]
q1 --> a2["Кодировка при сохранении"]
q1 --> a3["Интерпретация редактора"]
a1 --> a4["Кодовая страница консоли"]
a2 --> a5["Внутренний формат строк приложения"]
a3 --> a6["Локаль Linux"]
Рис. 2: Одного «появились кракозябры» недостаточно, чтобы назвать причину: смотрят, какой из этих шести слоёв разошёлся.
1.1 Термины, которые появятся сразу
Кратко соберём сокращения, которые дальше идут без пояснения.
| Термин | Полная форма | Значение в этой статье |
|---|---|---|
| BOM | Byte Order Mark | Несколько байт в начале файла. Сообщают читателю, какая это кодировка Unicode; для UTF-16 дополнительно показывают порядок байт. В UTF-8 BOM можно ставить, можно не ставить |
| кодовая страница (code page) | - | Механизм Windows, в котором номером задано, в какой устаревшей кодировке интерпретировать текст. CP932 японской Windows — один из таких номеров |
| ANSI | - | В Windows так называют «активную кодовую страницу в этот момент». Содержимое зависит от среды; в японской среде это CP932 |
| локаль (locale) | - | Набор умолчаний для языка, региона и кодировки. В Linux задаётся через LANG и LC_ALL и включает кодировку, как в ja_JP.UTF-8 |
| WSL | Windows Subsystem for Linux | Механизм запуска Linux поверх Windows. Предположения Windows и Linux живут на одной машине, поэтому инциденты из этой статьи здесь особенно вероятны |
| ETL | Extract / Transform / Load | Обработка, которая извлекает данные, преобразует их и записывает обратно. По пути файл читают и сохраняют заново, и это становится точкой смены кодировки |
Карта знаний этой статьи
Искажение кодировки в Windows возникает из расхождения предпосылок encode и decode: последовательность байтов, сохранённую в CP932, читают как UTF-8 — или наоборот, — и симптомы поломки зависят от направления. У Windows PowerShell значения encoding по умолчанию расходятся между 5.1 и 7 и новее: Out-File пишет UTF-16LE, Set-Content — CP932, и такие различия по путям становятся питательной средой для инцидентов. Сторона Linux по locale читает в предположении UTF-8, поэтому передача CP932 или UTF-8 с BOM ведёт к искажению кодировки и порче данных; особенно это легко случается в средах вроде WSL, где обе стороны живут рядом. Если исходная последовательность байтов ещё есть, её можно восстановить, перечитав и записав в правильном encoding через iconv или PowerShell; если же сохранить уже неверно прочитанное содержимое, восстановить нельзя. Статья рекомендует делать UTF-8 первым кандидатом для новых файлов и явно фиксировать сам encoding как контракт ввода-вывода.
flowchart LR
accTitle: Карта знаний: искажение кодировки Windows и взаимодействие с Linux
accDescr: Схема того, как симптомы искажения кодировки меняются в зависимости от того, читают ли ту же последовательность байтов как CP932 или как UTF-8; что BOM, UTF-16LE и кодовая страница консоли — отдельные слои; что encoding по умолчанию различается между версиями PowerShell; и связи восстановления через iconv и PowerShell с эксплуатационной политикой, в которой UTF-8 — первый кандидат
mojibake["mojibake (искажение кодировки)"]
cp932["CP932"]
utf_8["UTF-8"]
utf_16le["UTF-16LE"]
bom["BOM (Byte Order Mark)"]
encoding_overwrite_corruption["повреждение данных при пересохранении искажённого текста"]
console_code_page["кодовая страница консоли"]
windows_powershell_5_1["Windows PowerShell 5.1"]
powershell_7["PowerShell 7"]
iconv["iconv"]
powershell["PowerShell"]
linux_locale["locale в Linux"]
wsl["WSL (Windows Subsystem for Linux)"]
encoding_as_interface["encoding как контракт I/O"]
utf8_first_policy["политика UTF-8 first для новых файлов"]
cp932 -.->|"может вызвать"| mojibake
utf_8 -.->|"может вызвать"| mojibake
utf_16le -.->|"может вызвать"| mojibake
bom -.->|"может вызвать"| mojibake
mojibake -.->|"может вызвать"| encoding_overwrite_corruption
console_code_page -.->|"может вызвать"| mojibake
windows_powershell_5_1 -->|"использует"| utf_16le
windows_powershell_5_1 -->|"использует"| cp932
powershell_7 -->|"использует"| utf_8
iconv -.->|"снижает"| mojibake
powershell -.->|"снижает"| mojibake
mojibake -->|"проверяется"| iconv
linux_locale -.->|"использует"| utf_8
wsl -->|"использует"| linux_locale
wsl -.->|"может вызвать"| mojibake
encoding_as_interface -->|"рекомендуется для"| mojibake
utf8_first_policy -->|"рекомендуется для"| mojibake
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 17, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Что такое кракозябры на самом деле
Суть кракозябр довольно проста.
- Строку encode в какой-то кодировке в последовательность байт
- Эту последовательность байт decode в какой-то кодировке обратно в строку
- Если предположения encode и decode не совпали, результат читается как другая строка
flowchart TB
accTitle: Совпадение предположений encode и decode
accDescr: Когда строку encode в байты, а байты decode обратно в строку, при совпадении предположений получается исходная строка, а при расхождении она читается как другая.
e1["Строку encode в байты"] --> e2["Байты decode обратно в строку"]
e2 --> j1{"Предположения совпадают?"}
j1 -->|"совпадают"| r1["Возвращается исходная строка"]
j1 -->|"разошлись"| r2["Читается как другая строка"]
Рис. 3: Суть кракозябр в том, что предположения encode и decode не совпали.
Например, если сохранить あ в UTF-8, байты будут такими.
E3 81 82
Если прочитать эту последовательность как UTF-8, получится あ. Если в контексте CP932 — она выглядит как другая строка вроде 縺�. Это и есть кракозябры.
Важно, что здесь не «сломался японский текст», а разошлась интерпретация одних и тех же байт.
Посмотрим и в обратную сторону. Если сохранить あ в CP932, байты такие.
82 A0
Если эту последовательность пытаться читать как UTF-8, ни 0x82, ни 0xA0 не являются допустимым ведущим байтом UTF-8, поэтому оба превращаются в символ-заменитель и видно что-то вроде ��. Со стороны UTF-8 это вообще не является текстом.
На более длинном примере характер виден лучше. Если сохранить 日本語 в CP932, байты такие.
93 FA 96 7B 8C EA
Если прочитать это как UTF-8, получается что-то вроде ���{��. Здесь важно, что только четвёртый байт 0x7B проходит как ASCII {. Во втором байте CP932 иногда оказывается значение из диапазона ASCII, поэтому в «сломанном» результате всплывают знаки вроде { или \.
То есть симптомы зависят от направления.
| Фактические байты | Ожидание читателя | Как выглядит |
|---|---|---|
| UTF-8 | CP932 | Идут правдоподобные кандзи и катакана, вроде 縺 |
| CP932 | UTF-8 | Сплошные символы-заменители �, иногда вперемешку с ASCII-знаками вроде { |
«Если идут нечитаемые кандзи — UTF-8 читают как CP932; если сплошные символы-заменители — CP932 читают как UTF-8» — такую зацепку можно держать в голове, и диагностика идёт быстрее. Эта асимметрия полезна.
2.1 Если сломалось только отображение, ещё можно вернуть
У кракозябр есть стадия, на которой ещё можно вернуть как было. Например, если исходные байты не изменились, файл иногда восстанавливается, если открыть его заново в правильной кодировке.
Опасен обратный сценарий.
- Файл в UTF-8 ошибочно читают как CP932
- На экране видно что-то вроде
縺� - «Строку, которую видно», сохраняют как есть
- Исходные байты UTF-8 пропадают
С этой стадии это уже не сбой отображения, а повреждение данных.
flowchart TB
accTitle: Как сбой отображения превращается в повреждение данных
accDescr: Пока файл UTF-8 ошибочно прочитан как CP932 и на экране видны кракозябры, его ещё можно открыть заново; если видимую строку сохранить как есть, исходные байты UTF-8 пропадают, и это уже повреждение данных.
d1["Файл UTF-8 ошибочно читают как CP932"] --> d2["На экране видны кракозябры"]
d2 --> d3["Видимую строку сохраняют как есть"]
d3 --> d4["Исходные байты UTF-8 пропадают"]
d2 -.-> n1["Досюда ещё можно открыть заново и вернуть"]
d4 -.-> n2["Отсюда это повреждение данных"]
Рис. 4: Ошибочное чтение ещё обратимо; повреждение данных начинается в момент, когда сохранили неверно прочитанное содержимое.
2.2 Ещё опаснее — свести «непредставимые символы» к узкой кодовой странице
Другой типичный инцидент — когда строку Unicode сводят к устаревшей кодовой странице вроде CP932.
Если в строке есть символы, которых нет в целевой кодовой странице, происходит, например, такое:
- символ заменяется на
? - появляется символ-заменитель
� - символ превращается в похожий, но другой
- преобразование завершается ошибкой
Этот инцидент нужно оценивать не только по «читается / не читается», а по тому, возвращается ли строка к исходной при преобразовании туда и обратно (round-trip). Однажды потерянный символ потом не восстановить, даже узнав правильную кодировку.
flowchart TB
accTitle: Инцидент при сведении к узкой кодовой странице
accDescr: Если строку Unicode преобразовать в узкую кодовую страницу вроде CP932, символы, которых нет у получателя, заменяются или преобразование падает; round-trip исходную строку не возвращает, и однажды потерянные знаки нельзя восстановить, даже зная правильную кодировку.
u1["Строка Unicode"] --> u2["Преобразование в узкую кодовую страницу вроде CP932"]
u2 --> j1{"Символы, которых нет у получателя"}
j1 --> r1["Становятся ? или символом-заменителем"]
j1 --> r2["Другой знак / сбой преобразования"]
r1 --> k1["Round-trip исходную строку не возвращает"]
r2 --> k1
k1 -.-> n1["Потерянные символы восстановить нельзя"]
Рис. 5: Инцидент сведения к узкой кодовой странице смотрят не по «читается / не читается», а по тому, возвращается ли строка после round-trip.
3. Почему в Windows в этом так легко запутаться
Windows выглядит запутанным не просто потому, что он старый. Мир Unicode и мир устаревших кодовых страниц до сих пор живут рядом.
3.1 В Windows API сосуществуют ветка Unicode и ветка кодовых страниц
В Windows API есть две большие ветки.
- Семейство
W: wide character. Обрабатывает Unicode как UTF-16 - Семейство
A: ветка кодовых страниц, которую называют ANSI
То есть внутри Windows с самого начала есть и «путь Unicode», и «путь текущей активной кодовой страницы». Поэтому даже на одной и той же Windows предположение меняется в зависимости от того, через какой API или какой инструмент прошёл текст.
flowchart TB
accTitle: Две ветки Windows API
accDescr: В Windows API с самого начала сосуществуют семейство W, которое обрабатывает Unicode как UTF-16, и семейство A, которое работает через текущую активную кодовую страницу, поэтому предположение зависит от пройденного пути.
api["Windows API"] --> w1["Семейство W (wide character)"]
api --> a1["Семейство A (так называемый ANSI)"]
w1 --> w2["Unicode обрабатывается как UTF-16"]
a1 --> a2["Обработка через активную кодовую страницу"]
w2 --> z1["Предположение зависит от пройденного пути"]
a2 --> z1
Рис. 6: Внутри Windows с самого начала есть и путь Unicode, и путь кодовых страниц.
3.2 «Японский текст в Windows» — это не что-то одно
На практике вокруг японского текста в Windows чаще всего смешиваются следующие четыре варианта.
- CP932: часто встречается в legacy-тексте японской Windows
- UTF-8: всё чаще встречается в новых текстовых файлах, вебе и cross-platform сценариях
- UTF-16LE: до сих пор обычен в контексте инструментов и API Windows
- кодовая страница консоли: отдельный слой, который влияет на ввод-вывод
cmd.exeи части консольных инструментов
Здесь важно: chcp 65001 не делает файл UTF-8. Сменить кодовую страницу консоли и то, какие байты лежат в уже существующем файле, — разные вопросы.
flowchart TB
accTitle: chcp 65001 и существующий файл — разные вещи
accDescr: chcp 65001 меняет только кодовую страницу консоли; байты существующего файла не меняются, поэтому настройка консоли и содержимое файла — разные вопросы.
c1["Выполнить chcp 65001"] --> c2["Меняется кодовая страница консоли"]
f1["Байты существующего файла"] --> f2["Ничего не меняется"]
c2 --> n1["Консоль и файл — разные вопросы"]
f2 --> n1
Рис. 7: chcp 65001 меняет только интерпретацию консоли; байты файла остаются прежними.
Legacy-текст японской Windows часто небрежно называют «Shift_JIS», но на практике удобнее держать в уме имя CP932 — так меньше путаницы в разговоре. Как минимум явно видно, что речь про японскую устаревшую кодировку, пришедшую из Windows.
3.3 Имя файла и содержимое файла — разные вопросы
Если в Windows нормально видно японское имя файла, легко решить: «значит, и содержимое в порядке». Здесь как раз опасно.
- слой обработки пути / имени файла
- слой чтения содержимого файла
- слой отображения в консоли
Это три разных слоя.
Например, японский путь может обрабатываться без проблем, но если содержимое сохранено в CP932, а в Linux его читают как UTF-8 — оно ломается. И наоборот: даже если содержимое в UTF-8, при несовпадении кодовой страницы консоли ломается только отображение.
Связь слоёв выглядит так.
flowchart LR
accTitle: Кто пишет, байты файла и независимые читатели
accDescr: Факт — только байты файла; редактор, консоль, внутренности приложения и Linux читают их независимо, и успех на одном читателе не гарантирует остальные.
W["Кто пишет<br/>приложение / редактор / скрипт"] --> FB["Байты файла<br/>это единственный факт"]
FB --> R1["Читатель A: редактор<br/>автоопределение или заданная кодировка"]
FB --> R2["Читатель B: консоль<br/>кодовая страница ввода-вывода"]
FB --> R3["Читатель C: внутри приложения<br/>кодировка библиотеки по умолчанию"]
FB --> R4["Читатель D: Linux<br/>UTF-8 по локали"]
R1 --> S1["Ломается только отображение<br/>повторное сохранение превращает это в порчу"]
R2 --> S2["Ломается только отображение<br/>файл цел"]
R3 --> S3["Ломается результат обработки<br/>уходит дальше по цепочке"]
R4 --> S4["decode error или символ-заменитель"]
Рис. 8: Факт — только байты файла; редактор, консоль, приложение и Linux читают независимо друг от друга.
Смотреть нужно на два момента. Первый: сломаны байты посередине или читатель справа. Второй: четыре читателя справа независимы, и успех на одном не является гарантией для остальных трёх. «В консоли прочиталось, значит и в редакторе будет нормально» не работает именно из-за этой формы.
3.4 Значения по умолчанию в PowerShell и соседних инструментах тоже не согласованы
В Windows инциденты незаметно копит ещё одно: при, казалось бы, одной и той же «записи текста» выходные байты зависят от пути записи.
Особенно стоит смотреть сюда.
- У Windows PowerShell 5.1 нет согласованной кодировки по умолчанию
- Часть cmdlet и перенаправлений создаёт UTF-16LE
- Другим путём используется активная кодовая страница ANSI
- Начиная с PowerShell 7 по умолчанию UTF-8 без BOM
То есть одной фразы «текст из PowerShell» недостаточно, чтобы назвать кодировку. Нужно смотреть, какой версии, каким cmdlet и каким путём записи это сделали.
Какие байты даёт каждый путь записи, собрано в about_Character_Encoding на Microsoft Learn. Если выписать только часто используемое, получается так.
| Путь записи | По умолчанию в Windows PowerShell 5.1 | По умолчанию в PowerShell 7 |
|---|---|---|
Out-File, >, >> |
UTF-16LE (с BOM) | UTF-8 без BOM |
Set-Content, Add-Content (новый или пустой файл) |
ANSI = активная кодовая страница. В японской среде — CP932 | UTF-8 без BOM |
Export-Csv |
ASCII. Не-ASCII символы теряются | UTF-8 без BOM |
Export-Clixml, New-ModuleManifest |
UTF-16LE | UTF-8 без BOM |
New-Item -Type File -Value |
UTF-8 без BOM | UTF-8 без BOM |
Start-Transcript |
UTF-8 with BOM | UTF-8 без BOM |
Есть разница и на стороне чтения. Когда читают файл без BOM, Get-Content в 5.1 считает его ANSI, а Import-Csv и Select-String — UTF-8. Внутри одной сессии предположения уже разошлись.
На практике бьёт сильнее всего другое: при одной и той же «записи текста» Out-File даёт UTF-16LE, а Set-Content — CP932. «Похожий на двоичный текст с кучей NUL byte» из раздела 4.3 почти всегда приходит из умолчаний > или Out-File.
flowchart TB
accTitle: Асимметрия чтения в Windows PowerShell 5.1
accDescr: Когда файл без BOM читают в Windows PowerShell 5.1, Get-Content считает его ANSI, а Import-Csv и Select-String — UTF-8, так что внутри одной сессии предположения уже разошлись.
b1["Файл без BOM"] --> g1["Читают через Get-Content"]
b1 --> i1["Читают через Import-Csv или Select-String"]
g1 --> g2["Считают ANSI"]
i1 --> i2["Считают UTF-8"]
g2 --> z1["Внутри одной сессии предположения расходятся"]
i2 --> z1
Рис. 9: В 5.1 у одного и того же файла без BOM ожидаемая кодировка зависит от cmdlet, которым его читают.
Ещё одно: в 5.1 даже при -Encoding UTF8 получается с BOM. Если в 5.1 нужно записать UTF-8 без BOM, пишут со стороны .NET.
# Запись UTF-8 без BOM в Windows PowerShell 5.1
$text = "日本語を含む本文"
[System.IO.File]::WriteAllText(
"C:\work\output.txt", $text,
(New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false))
Аргумент UTF8Encoding, равный $false, как раз и есть указание не ставить BOM. В PowerShell 7 тот же результат даёт -Encoding utf8NoBOM.
4. Типичные инциденты при связке с Linux
Не редкость, что то, что внутри одной Windows кое-как работало, ломается, как только в цепочку входит Linux. Причина простая: в Linux предположение UTF-8 сильное.
4.1 Текст, сохранённый в Windows в CP932, Linux читает как UTF-8
Самый частый инцидент.
- Legacy-приложение Windows или старый процесс эксплуатации пишет CSV / TXT / log в CP932
- Скрипты и инструменты в Linux по локали читают с предположением UTF-8
- В итоге decode error,
�или бессмысленная строка
Виноват здесь не инструмент Linux. Корень в том, что у полученных байт не было договорённости о кодировке.
flowchart TB
accTitle: Linux читает текст CP932 как UTF-8
accDescr: Legacy-приложение или старый процесс Windows пишет CSV или журнал в CP932 и передаёт байты без договорённости о кодировке, а скрипт или инструмент Linux по локали читает с предположением UTF-8 — и получается decode error или символ-заменитель.
p1["Legacy-приложение пишет CSV / log в CP932"] --> p2["Передают без договорённости о кодировке"]
p2 --> p3["Linux по локали читает как UTF-8"]
p3 --> p4["decode error / символ-заменитель"]
p2 -.-> n1["Корень — отсутствие договорённости"]
Рис. 10: Виноват не инструмент Linux, а то, что к байтам не приложили договорённость о кодировке.
4.2 UTF-8 без BOM, сделанный в Linux / VS Code, Windows принимает за ANSI
Бывает и обратный инцидент.
- В Linux или VS Code делают script / config / text в UTF-8 без BOM
- Windows PowerShell 5.1 или устаревший инструмент считает файл без BOM кодовой страницей ANSI
- Ломаются только строки с японским текстом или другими не-ASCII символами
Здесь часто винят UTF-8, но настоящая причина в том, что в цепочке есть читатель, который не умеет правильно распознать UTF-8 без BOM.
flowchart TB
accTitle: UTF-8 без BOM принимают за ANSI
accDescr: Файл UTF-8 без BOM, сделанный в Linux или VS Code, Windows PowerShell 5.1 или устаревший инструмент считает кодовой страницей ANSI, и ломаются только строки с японским текстом и другим non-ASCII.
v1["В Linux / VS Code делают UTF-8 без BOM"] --> v2["Читают 5.1 или устаревший инструмент"]
v2 --> v3["Файл без BOM считают стороной ANSI"]
v3 --> v4["Ломаются только строки с non-ASCII"]
v3 -.-> n1["Причина — читатель, который не распознаёт"]
Рис. 11: В обратном инциденте причина — читатель, который не умеет распознать UTF-8 без BOM.
4.3 Windows пишет UTF-16LE, а в Linux это «не похоже на текст»
Это тоже встречается часто.
- Часть вывода Windows PowerShell 5.1 или устаревший инструмент пишет UTF-16LE
- Текстовые инструменты Linux ожидают однобайтовый поток UTF-8
- В результате получается «похожий на двоичный текст» с кучей NUL byte
Сам UTF-16LE не плох. Но во многих случаях он плохо стыкуется с предположением «просто отдать это текстовым инструментам Linux».
flowchart TB
accTitle: UTF-16LE в Linux выглядит как двоичные данные
accDescr: UTF-16LE, который пишет часть вывода Windows PowerShell 5.1 или устаревший инструмент, на текстовых инструментах Linux, ожидающих однобайтовый поток UTF-8, выглядит как двоичный текст с кучей NUL byte.
u1["Часть вывода 5.1 или устаревший инструмент"] --> u2["Пишет UTF-16LE"]
u2 --> u3["Текстовый инструмент Linux ожидает поток по 1 байту"]
u3 --> u4["Вперемешку NUL byte, выглядит как двоичные данные"]
Рис. 12: Сам UTF-16LE не плох, но с предположением текстовой обработки Linux он не стыкуется.
4.4 Нестыковки даёт и наличие или отсутствие BOM
BOM — это не сама кодировка, но на практике он заметно влияет.
- Части инструментов Windows BOM помогает
- Часть инструментов Linux воспринимает BOM как лишние байты в начале
- В итоге ломается только начало первого столбца или первой строки, появляется невидимый мусор, расходятся результаты сравнения
Особенно для UTF-8: даже если формально это один и тот же UTF-8, байты с BOM и без BOM — разные вещи. Одной фразы «перешли на UTF-8» для правила эксплуатации мало: решена только половина вопроса.
flowchart TB
accTitle: Нестыковки из-за наличия или отсутствия BOM
accDescr: Даже у одного UTF-8 байты с BOM и без BOM разные: части инструментов Windows BOM помогает, а часть инструментов Linux считает его лишними байтами в начале, поэтому ломается только начало.
b0["Даже у одного UTF-8 BOM да / нет — разные байты"] --> w1["Часть инструментов Windows"]
b0 --> l1["Часть инструментов Linux"]
w1 --> w2["С BOM становится проще"]
l1 --> l2["Считают лишними байтами в начале"]
l2 --> l3["Ломается только начало / появляется невидимый мусор"]
Рис. 13: «Сделали UTF-8» недостаточно; правило появляется, только когда решили и вопрос BOM.
4.5 Если верить тому, что показывает консоль, легко запутаться
При работе на стыке Windows и Linux опасна ещё и консоль.
- У консоли Windows есть кодовые страницы ввода и вывода
- Терминал Linux чаще работает с предположением UTF-8 локали
- Если путь идёт через WSL, SSH, контейнеры и CI, маршрутов отображения становится больше
В таком состоянии суждения «в консоли прочиталось, значит и с файлом всё в порядке» или «в консоли поехало, значит файл повреждён» легко оказываются ошибочными. Безопаснее проверять отдельно, сломано ли то, что видно на экране, или сломаны сохранённые байты.
4.6 Типичные инциденты таблицей
| Ситуация | Фактические байты | Ожидание читателя | Типичный симптом |
|---|---|---|---|
| CSV, который сохранило legacy-приложение Windows | CP932 | Linux — UTF-8 | �, decode error, бессмысленный японский текст |
| Файл, сделанный в Linux / VS Code | UTF-8 без BOM | Windows PowerShell 5.1 считает ANSI | Ломаются только строки с японским текстом |
| Часть вывода Windows PowerShell 5.1 | UTF-16LE или ANSI | Linux ждёт текст UTF-8 | Примесь NUL byte, поведение как у двоичного файла |
| Файл UTF-8 with BOM | UTF-8 + BOM | Unix-инструменты ждут plain UTF-8 | Ломается только первый столбец, появляются лишние знаки |
| Верить только отображению в консоли | У файла и консоли разные предположения | Исследователь судит только по экрану | Промах в диагностике причины |
5. Диагностику кракозябр ведём этими четырьмя вопросами
Если диагностика кракозябр зашла в тупик, быстрее всего вернуться к следующим четырём вопросам.
5.1 Что представляют собой исходные байты
Сначала смотрят «какие байты сейчас в этом файле». Нужна установка смотреть на байты, а не на внешний вид.
- Это UTF-8?
- UTF-8 with BOM?
- CP932?
- UTF-16LE?
- Не пересохранили ли файл по пути, так что он стал уже другим?
5.2 Кто первым записал и с каким предположением
Дальше определяют, кто записал файл первым.
- Legacy-приложение Windows?
- PowerShell 5.1 или 7?
- Скрипт Linux?
- VS Code?
- Экспорт из Excel?
- Какой-то middleware / batch / CI?
Пока это размыто, оценка кодировки превращается в лотерею.
5.3 Кто сейчас читает и с каким предположением
Нужен не только тот, кто записал, но и предположение читателя.
- Редактор делает auto-detect?
- PowerShell смотрит на BOM?
- Linux по локали считает файл UTF-8?
- Библиотека берёт кодировку по умолчанию?
- Явно заданы
Encoding.UTF8илиcp932?
Кракозябры почти всегда возникают именно здесь.
5.4 Неверно прочитанное содержимое уже сохранили?
Наконец проверяют, остановился ли ущерб на отображении.
- Байты ещё исходные?
- Кто-то сохранил содержимое, которое выглядит сломанным?
- В diff не попали
?или�? - Весь текст не переписали в другой кодировке?
Если эти четыре вопроса закрыты, причина, как правило, видна.
flowchart TB
accTitle: Четыре вопроса диагностики кракозябр
accDescr: Если по порядку закрыть, что за исходные байты, кто и с каким предположением записал первым, кто и с каким предположением читает сейчас и сохранено ли уже неверно прочитанное, причина кракозябр обычно видна.
q1["Вопрос 1: что за исходные байты"] --> q2["Вопрос 2: кто первым записал и с каким предположением"]
q2 --> q3["Вопрос 3: кто сейчас читает и с каким предположением"]
q3 --> q4["Вопрос 4: неверно прочитанное уже сохранили?"]
q4 --> r1["Причина видна"]
Рис. 14: Если диагностика зашла в тупик, быстрее всего вернуться к этим четырём вопросам и закрывать их по порядку.
6. Как чинить сломанный файл
Когда причина видна, следующий шаг — восстановление. Первым делом решают, остались ли ещё исходные байты.
- Исходные байты на месте: читают заново в правильной кодировке и записывают в целевую кодировку — так возвращают. Это процедура этой главы
- Результат неверного чтения уже сохранили: остаётся только откат из резервной копии или истории Git. Символы, ставшие
�или?, не восстановить, даже если правильная кодировка уже известна
Поэтому самое первое действие — снять копию того, с чем работаете. Преобразование делают по копии, исходный файл не трогают.
flowchart TB
accTitle: Первое ветвление восстановления
accDescr: Сначала снимают копию и проверяют, остались ли исходные байты: если да — читают заново в правильной кодировке и записывают; если результат неверного чтения уже сохранили, остаётся только резервная копия или история Git, а символы-заменители не восстановить.
s1["Сначала снять копию"] --> j1{"Исходные байты ещё на месте?"}
j1 -->|"на месте"| r1["Прочитать заново в правильной кодировке и записать"]
j1 -->|"уже сохранили и потеряли"| r2["Вернуть из резервной копии или истории Git"]
r2 -.-> n1["Символы, ставшие заменителями, не восстановить"]
Рис. 15: Восстановление начинают со снятия копии, а дальше путь зависит от того, остались ли исходные байты.
6.1 Если вы в Linux — iconv
Чтобы сделать файл CP932 файлом UTF-8, самый прямой путь — iconv.
# CP932 -> UTF-8
iconv -f CP932 -t UTF-8 input.csv > output.csv
Если кодировка источника указана неверно, преобразование останавливается примерно так.
iconv: illegal input sequence at position 0
Сам факт остановки — информация «это не та кодировка», поэтому быстрее перебирать кандидатов. Если же не останавливается ни на чём, файл, возможно, состоит только из ASCII.
Чтобы свести UTF-16LE к UTF-8, берут -f UTF-16LE. Но если файл с BOM явно преобразовать как -f UTF-16LE, BOM иногда остаётся в выводе как один символ U+FEFF. Если обработку BOM хотите отдать iconv, используйте -f UTF-16 и после преобразования обязательно проверьте первый символ.
6.2 Если вы в Windows — PowerShell
Начиная с PowerShell 6.2 в -Encoding можно передать номер кодовой страницы напрямую. Для CP932 это 932.
# PowerShell 6.2 и новее. CP932 -> UTF-8 без BOM
Get-Content -Path .\input.csv -Encoding 932 |
Set-Content -Path .\output.csv -Encoding utf8NoBOM
Обратно: если файл UTF-8, сделанный в Linux, нужно отдать получателю, который читает только CP932, делают так.
Get-Content -Path .\input.csv -Encoding utf8 |
Set-Content -Path .\output.csv -Encoding 932
У этой записи есть два побочных эффекта.
Get-Contentрежет по строкам,Set-Contentзаново ставит перевод строки после каждой. То есть символы перевода строки приводятся к одному виду- Даже если в исходном файле в конце не было перевода строки, в выводе он появится
Если нужно сохранить байты вместе с переводами строк, файл обрабатывают целиком, не режа на строки.
# CP932 -> UTF-8 без BOM, переводы строк без изменений
$text = [System.IO.File]::ReadAllText(
"C:\work\input.csv", [System.Text.Encoding]::GetEncoding(932))
[System.IO.File]::WriteAllText(
"C:\work\output.csv", $text,
(New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false))
GetEncoding(932) в Windows PowerShell 5.1 работает как есть. Если в PowerShell 7 в вашей среде это даёт исключение, один раз выполните следующее и повторите.
[System.Text.Encoding]::RegisterProvider(
[System.Text.CodePagesEncodingProvider]::Instance)
Ставить BOM или нет, как в 7.1, решают под получателя. В примере выше аргумент UTF8Encoding, равный $true, даёт вариант с BOM.
6.3 Что обязательно смотреть после преобразования
Преобразование не заканчивается на «ошибки не было». Как минимум смотрят следующее.
- Читаются ли 2–3 характерные строки с японским текстом
- Не прибавилось ли
?или�. Если прибавилось, эти символы уже потеряны - Не изменилось ли число строк
- BOM и символ перевода строки совпадают с ожиданием получателя
- Не изменился ли размер файла слишком резко. При переходе с CP932 на UTF-8 японские фрагменты растут с 2 байт до 3 байт, поэтому небольшое увеличение — норма
Особенно важен второй пункт. Когда UTF-8 с символами, которых нет в CP932, сводят к CP932, информация теряется обязательно. Как в 2.2, проверяйте и то, возвращается ли текст после round-trip.
flowchart TB
accTitle: Что обязательно смотреть после преобразования
accDescr: Преобразование не заканчивается на отсутствии ошибки: проверяют, читаются ли характерные японские строки, не прибавилось ли ? и символов-заменителей, совпадают ли число строк, BOM, перевод строки и размер, и если заменителей стало больше — эти символы уже потеряны.
c1["Преобразование завершилось без ошибки"] --> c2["Прочитать 2–3 характерные японские строки"]
c2 --> c3["Не прибавилось ли ? и символов-заменителей"]
c3 --> c4["Проверить число строк, BOM, перевод строки, размер"]
c3 -.-> n1["Если прибавилось — эти символы уже потеряны"]
Рис. 16: «Ошибки не было» преобразования не закрывает; проверка содержимого входит в тот же набор.
6.4 Массовое преобразование выносят в «задачу миграции»
Наконец, про эксплуатацию. Починить один сломанный файл и перевести весь репозиторий с CP932 на UTF-8 — разная работа. Второе, как в 7.2 следующей главы, не делают заодно с обычными правками функций: планируют как отдельную задачу миграции.
7. Правила работы, которые снижают число инцидентов
Дальше — практическая часть. В проектах на стыке Windows и Linux число инцидентов заметно падает, если следующие правила решить заранее.
7.1 Для новых файлов UTF-8 — первый вариант
Для нового текстового файла разумно первым вариантом брать UTF-8. Но на этом останавливаться нельзя: нужно решить и как быть с BOM.
Удобно решать так.
- Текст, который чаще читают в Linux: базово UTF-8 без BOM
- Скрипты, которые читают устаревшие инструменты Windows или Windows PowerShell 5.1: наличие или отсутствие BOM явно задавать под получателя
- Если есть явный получатель, которому нужен UTF-16LE, это требование записывают в спецификацию
Если написать только «унифицировали на UTF-8», позже поссорятся из-за BOM.
flowchart TB
accTitle: Как выбирать кодировку нового файла
accDescr: Для нового текстового файла UTF-8 берут первым вариантом и на этом не останавливаются: если чаще читает Linux — базово UTF-8 без BOM, если устаревший инструмент или Windows PowerShell 5.1 — BOM явно под получателя, если нужен UTF-16LE — требование пишут в спецификацию.
s1["Новый текстовый файл"] --> s2["UTF-8 как первый вариант"]
s2 --> j1{"Кто будет читать?"}
j1 -->|"чаще Linux"| r1["Базово UTF-8 без BOM"]
j1 -->|"5.1 или устаревший инструмент"| r2["BOM явно под получателя"]
j1 -->|"получатель, которому нужен UTF-16LE"| r3["Требование записать в спецификацию"]
Рис. 17: Для нового файла UTF-8 берут первым вариантом и доводят решение по BOM до требований получателя.
7.2 Существующие legacy-файлы держат как есть до явной задачи миграции
Если существующий файл в CP932, безопаснее не переводить его в UTF-8 заодно с обычными правками функций.
Безопасная схема такая.
- Существующие файлы сохраняют исходные кодировку / BOM / переводы строк
- Смену кодировки выносят в задачу миграции
- Массовое преобразование делают только после проверки объектов, зоны влияния и потребителей ниже по цепочке
Многие инциденты с кракозябрами начинаются с доброго «заодно переведём в UTF-8».
7.3 Кодировку рассматривают как часть интерфейса
Для CSV, TXT, журналов, файлов настроек и простых protocol интерфейсом является не только содержимое, но и сама кодировка.
Например, в спецификации стоит зафиксировать как минимум следующее.
- Этот файл — UTF-8, CP932 или UTF-16LE
- Если UTF-8, есть ли BOM
- Перевод строки — LF или CRLF
- Кто producer, а кто consumer — Linux или Windows
- Не пересохраняет ли промежуточный batch или ETL
«Передаём как text» спецификацией не является.
7.4 Умолчаниям не доверяют — при записи кодировку указывают явно
И в коде, и в скриптах кодировку безопаснее указывать явно.
Опасны такие ходы мысли.
- Сохранять с умолчаниями
- Решить, что «под ОС, наверное, само получится нормально»
- Считать, что раз в консоли прочиталось, то и с файлом всё в порядке
- Полагаться на то, что есть auto-detect
Умолчания обычным образом различаются между Windows и Linux, PowerShell 5.1 и 7, редакторами и runtime. Пока кодировка не указана явно, легко оказаться в ситуации, когда всё просто случайно работает.
7.5 Консоль и файл проверяют раздельно
Неброское, но действенное правило.
- Проверка отображения в консоли
- Проверка повторным открытием файла
Эти два действия разделяют.
Даже если chcp и отображение терминала совпали, это ничего не даёт, если сохранённый файл в другой кодировке. И наоборот: даже если файл в порядке, при несовпадении кодовой страницы отображения консоли ломается только внешний вид.
7.6 Git кодировку не чинит
Неброско, но важно.
Git по сути только отслеживает байты. То есть сломанные байты он так же добросовестно кладёт в историю.
Поэтому когда:
- ничего не меняли, а вырос огромный diff
- загадочный diff только по строкам с японским текстом
- изменилась только первая строка
- перевод строки и кодировка изменились вместе
— в таких случаях раньше, чем править содержимое, стоит заподозрить инцидент перекодирования (re-encoding).
flowchart TB
accTitle: Git кодировку не чинит
accDescr: Git по сути только отслеживает байты и кладёт в историю даже сломанные байты, поэтому огромный diff «из ничего» или загадочный diff только по японским строкам стоит сначала считать инцидентом re-encoding, а не правкой содержимого.
g1["Git только отслеживает байты"] --> g2["Сломанные байты тоже попадают в историю как есть"]
g2 --> g3["Огромный diff / загадочный diff только по японским строкам"]
g3 --> g4["Раньше правки содержимого подозревать re-encoding"]
Рис. 18: Git добросовестно записывает и сломанные байты, поэтому загадочный diff сначала проверяют как re-encoding.
8. Минимальный чек-лист
Чек-лист, который стоит зафиксировать в первую очередь в проектах, где смешаны Windows и Linux.
flowchart TB
accTitle: Поток чек-листа
accDescr: До правки проверяют текущую кодировку, BOM и перевод строки, во время правки не полагаются на умолчания и auto-detect, после правки снова открывают файл и смотрят diff; массовое преобразование выносят в отдельную задачу миграции.
c1["До правки: текущая кодировка, BOM, перевод строки"] --> c2["Во время правки: не оставлять на умолчания и auto-detect"]
c2 --> c3["После правки: снова открыть и посмотреть diff"]
c3 -.-> t1["Массовое преобразование вынести в задачу миграции"]
Рис. 19: Проверку делят на «до / во время / после правки», а массовое преобразование выносят в отдельную задачу.
8.1 До правки
- Какая сейчас кодировка у этого файла
- Есть ли BOM
- Перевод строки — LF или CRLF
- Записаны ли 2–3 характерные строки с японским текстом
- Известно ли, кто конечный consumer — Linux или Windows
8.2 Во время правки
- Нет ли записи, которая зависит от кодировки по умолчанию
- Не сохраняете ли вы, полагаясь на auto-detect
- Не используете ли небрежно пути через PowerShell или перенаправление оболочки
- Не успокаиваетесь ли одним «на экране читается»
8.3 После правки
- Проверили ли повторным открытием после сохранения
- Не сломались ли характерные строки ни в Linux, ни в Windows
- Не прибавилось ли в diff
?или� - Не сломались ли только первая строка или первый столбец
- Не стал ли diff большим только из-за BOM / перевода строки
8.4 Что делать как задачу миграции
- Массовое преобразование CP932 → UTF-8
- Унификация политики BOM для UTF-8
- Инвентаризация скриптов, рассчитанных на PowerShell 5.1
- Документирование путей передачи текста через CI / контейнеры / WSL / SSH
- Унификация настроек сохранения в редакторах, форматтерах и batch-скриптах
9. Итог
Если описать проблему кодировок Windows одной фразой, суть в том, что мир Unicode и мир устаревших кодовых страниц до сих пор живут рядом.
А инцидентов при связке с Linux становится больше потому, что Linux чаще исходит из UTF-8, и на этом фоне сразу всплывают CP932 и UTF-16 из Windows, кодовая страница консоли и различия версий PowerShell.
Имеет смысл запомнить следующие пять пунктов.
- Кракозябры — это расхождение в интерпретации байт
- Сбой отображения и повреждение данных — разные вещи
- В Windows слои файла, редактора, консоли и API рассматривают раздельно
- Для текста, которым обмениваются с Linux, UTF-8 — первый вариант
- Преобразование существующих legacy-файлов отделяют от обычных правок
Если фразу «в Windows появились кракозябры» брать буквально, тема слишком широкая. Но если резать её этими четырьмя вопросами —
- Что за исходные байты
- Кто и как записал
- Кто и как прочитал
- Уже сохранили?
— картина заметно проясняется.
Кодировка текста — тема неброская, но между Windows и Linux это сам I/O-контракт. Не оставлять его размытым — самая действенная мера.
10. Справочные материалы
Windows / Microsoft
- Code Pages - Win32 apps | Microsoft Learn
- Code Page Identifiers - Win32 apps | Microsoft Learn
- Unicode in the Windows API - Win32 apps | Microsoft Learn
- Console Code Pages - Windows Console | Microsoft Learn
- chcp | Microsoft Learn
- Use UTF-8 code pages in Windows apps | Microsoft Learn
PowerShell / VS Code
- about_Character_Encoding | Microsoft Learn
- Understanding file encoding in VS Code and PowerShell | Microsoft Learn
GNU / Linux locale
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Кодировки текста и переводы строк в Windows — основы искажения текста и CRLF/LF
Разбираем часто путаемые в Windows Shift_JIS / UTF-8 / UTF-16, искажение кодировки и разницу между CRLF и LF так, чтобы по ним можно было...
Правила инструкций, которые уменьшают кракозябры Codex в Windows
Практические правила инструкций для Codex в Windows: не сохранять японские файлы наугад, сохранять исходную кодировку существующих файлов...
Японские шрифты в бизнес-приложениях: JIS2004, IVS и пользовательские символы
«На экране и в печатной форме 葛 выглядит по-разному.» «Иероглиф в имени не отображается.» Такие сбои в бизнес-системах становятся понятны...
WMI/CIM из C# и PowerShell — практическое руководство по сведениям об оборудовании, мониторингу процессов и удалённым запросам
WMI/CIM — стандартный способ получить серийный номер ПК, следить за свободным местом на диске и ловить запуск процессов. Разбираем команд...
Групповая политика (GPO) на практике: как применяется, как проверить и когда брать Intune
Работаете в AD и не до конца понимаете, что значит «это настроено через GPO»? Разбираем устройство групповой политики и порядок LSDOU, ка...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Технические консультации и ревью дизайна
В проектах, где Windows и Linux по-разному предполагают кодировку CSV, журналов и файлов настроек, инцидентов меньше, если заранее зафиксировать I/O-контракт и правила работы.
Разработка приложений для Windows
В бизнес-инструментах для Windows часто соседствуют CP932 и UTF-8, и то, как кодировки заложены в проект, напрямую влияет на сопровождаемость.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Почему в Windows появляются кракозябры (mojibake)?
- Почти всегда потому, что одну и ту же последовательность байт прочитали как другую кодировку или результат неверного чтения сохранили уже в другой кодировке. Например, байты (E3 81 82), которыми символ «あ» сохранили в UTF-8, в контексте CP932 выглядят как другая строка вроде «縺». Дело не в сложности японского языка: не совпали предположения encode и decode.
- Можно ли вернуть файл с кракозябрами к исходному виду?
- Если исходные байты не изменились, файл иногда удаётся восстановить, открыв его заново в правильной кодировке. Опасно другое: сохранить уже неверно прочитанное, «сломанное» на экране содержимое. С этой стадии это уже не сбой отображения, а повреждение данных. Если строку Unicode свести к узкой кодовой странице вроде CP932 и символы превратились в «?» или символ-заменитель, однажды потерянные знаки потом не восстановить, даже узнав правильную кодировку.
- Если выполнить chcp 65001, файлы тоже станут UTF-8?
- Нет. Сменить кодовую страницу консоли и то, какие байты лежат в уже существующем файле, — разные вещи. В Windows кодировка самого файла, интерпретация редактора, входная и выходная кодовые страницы консоли и внутренний формат строк приложения — разные слои, и их нужно рассматривать по отдельности. Суждение «в консоли прочиталось, значит и с файлом всё в порядке» легко оказывается ошибочным, поэтому проверку отображения в консоли и проверку повторным открытием файла стоит разделять.
- Какие безопасные правила обмена текстом между Windows и Linux?
- Для новых файлов первым вариантом брать UTF-8 и сразу решить, как быть с BOM. Текст, который чаще читают в Linux, по умолчанию делать UTF-8 без BOM; если его читают устаревшие инструменты вроде Windows PowerShell 5.1, наличие или отсутствие BOM явно задавать под получателя. Существующие файлы в CP932 не конвертировать заодно с обычными правками — это отдельная явная задача миграции. У CSV и журналов сама кодировка является интерфейсом, поэтому кодировку, BOM и символ перевода строки стоит записать в спецификацию: так инцидентов меньше.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.