Когда я выполняю следующий простой сценарий в PowerShell 7.1, я получаю (правильное) значение 3, независимо от того, является ли кодировка сценария Latin1 или UTF8.
'Bär'.length
Это меня удивляет, потому что у меня сложилось (очевидно неправильное) впечатление, что кодировка по умолчанию в PowerShell 5.1 — UTF16-LE, а в PowerShell 7.1 — UTF-8.
Поскольку оба сценария оценивают выражение как 3, я вынужден заключить, что PowerShell 7.1 применяет некоторый эвристический метод для определения кодировки сценария при его выполнении.
Верен ли мой вывод, и это где-то задокументировано?
Кодировка не имеет отношения к этому случаю: вы вызываете string.Length
, который задокументирован для возврата количества кодовых единиц UTF-16. Это примерно соответствует буквам (если игнорировать комбинирование символов и высокие кодовые точки, такие как смайлики).
Кодирование вступает в игру только при неявном или явном преобразовании в/из байтового массива, файла или p/invoke. Это не влияет на то, как .Net хранит данные, поддерживающие строку.
Говоря о кодировке файлов PS1, это зависит от версии. Старые версии имеют резервную кодировку Encoding.ASCII
, но будут учитывать спецификацию для UTF-16 или UTF-8. Более новые версии используют UTF-8 в качестве запасного варианта.
По крайней мере, в версии 5.1.19041.1 загрузка файла 'Bär'.Length
(27 42 C3 A4 72 27 2E 4C 65 6E 67 74 68
) и запуск его с помощью . .\Bar.ps1
приведет к печати 4.
Если тот же файл сохранен как Windows-1252 (27 42 E4 72 27 2E 4C 65 6E 67 74 68
), то будет напечатано 3.
tl;dr: string.Length
всегда возвращает количество кодовых единиц UTF-16. Файлы PS1 должны быть в кодировке UTF-8 с BOM для совместимости между версиями.
Строка из файла, и я нигде не указываю кодировку файла. Таким образом, PowerShell каким-то образом должен определить кодировку файла.
Правильно - что может привести к отображению неправильного символа, если Powershell загрузит файл сценария в неправильной кодировке (или если ваша консоль настроена на неправильную кодировку), но это не приведет к тому, что длина будет другой. Количество персонажей остается прежним.
Я думаю, что без спецификации PS 5 предполагает ansi или windows-1252, а PS 7 предполагает utf8 no bom. Этот файл, сохраненный как ansi в блокноте, работает в PS 5, но не идеально в PS 7. Точно так же, как файл utf8 no bom со специальными символами, не будет работать идеально в PS 5. Файл utf16 ps1 всегда будет иметь спецификацию или подпись кодировки. Строка powershell в памяти всегда будет utf16, но считается, что символ имеет длину 1, за исключением смайликов. Если у вас есть emacs, esc-x hexl-mode — хороший способ взглянуть на него.
'¿Cómo estás?'
format-hex file.ps1
Label: C:\Users\js\foo\file.ps1
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 27 BF 43 F3 6D 6F 20 65 73 74 E1 73 3F 27 0D 0A '¿Cómo estás?'��
У меня сложилось (по-видимому, ошибочное) впечатление, что кодировка по умолчанию в PowerShell 5.1 — UTF16-LE, а в PowerShell 7.1 — UTF-8.
Есть две различные кодировки символов по умолчанию, которые следует учитывать:
Кодировка вывода по умолчанию, используемая различными командлетами (Out-File
, Set-Content
) и операторами перенаправления (>
, >>
) при записи файла.
Эта кодировка сильно различается для командлетов в Windows PowerShell (версии PowerShell до 5.1), но теперь, к счастью, по умолчанию постоянно используется UTF-8 без спецификации в PowerShell [Core] v6+ — см. этот ответ для получения дополнительной информации.
Примечание. Эта кодировка всегда не связана с кодировкой файла, из которого данные могли быть считаны изначально, поскольку PowerShell не сохраняет эту информацию и никогда не передает текст в виде необработанных байтов — текст всегда преобразуется в .NET ([string]
, System.String) с помощью PowerShell перед дальнейшей обработкой данных.
Входная кодировка по умолчанию при чтении файла - например, как исходный код, прочитанный движком, так и файлы, прочитанные Get-Content
, которая применяется только к файлам без спецификации (поскольку файлы со спецификациями всегда правильно распознаются).
При отсутствии спецификации:
Windows PowerShell предполагает активную кодовую страницу ANSI системы, например Windows-1252 в системах с английским языком США. Обратите внимание, что это означает, что системы с разными активными системными локалями (настройками для приложений, отличных от Unicode) могут по-разному интерпретировать данный файл.
PowerShell [Core] v6+ более разумно предполагает UTF-8, которая способна представлять все символы Unicode и интерпретация которых не зависит от системных настроек.
Обратите внимание, что это фиксированные, детерминированные допущения — эвристика не используется.
В результате для исходного кода разных редакций лучше всего использовать кодировку UTF-8 с BOM, которую обе редакции правильно распознают.
Что касается файла исходного кода, содержащего 'Bär'.length
:
Если кодировка файла исходного кода распознана правильно, результатом всегда будет 3
, учитывая, что создается экземпляр строки .NET ([string]
, System.String), который в памяти всегда состоит из кодовых единиц UTF-16 ( [char]
, System.Char), и учитывая, что .Length
подсчитывает количество этих кодовых единиц.[1]
Не учитывать поврежденные файлы (например, файл UTF-16 без спецификации или файл со спецификацией, которая не соответствует фактической кодировке):
Единственный сценарий, в котором .Length
не возвращает 3
:
В Windows PowerShell, если файл был сохранен как файл UTF-8 без спецификации.
ä
(ЛАТИНСКАЯ СТРОЧНАЯ БУКВА A С ДИЕРЕЗИСОМ, U+00E4 ) кодируется как 2 байта в UTF-8, 0xc3
и 0xa4
, результирующая строка имеет 4 символа.Bär
Напротив, в PowerShell [Core] v6+ файл без спецификации, который был сохранен на основе активной страницы ANSI (или OEM-кода) (например, с Set-Content
в Windows PowerShell), вызывает все символы, отличные от ASCII (в 8-разрядном коде). диапазон) считать недопустимыми символами, потому что они не могут быть интерпретированы как UTF-8.
�
(REPLACEMENT CHARACTER, U+FFFD) — другими словами: информация теряется.B�r
, а ее .Length
по-прежнему 3
.[1] A single UTF-16 code unit is capable of directly encoding all 65K characters in the so-called BMP (Basic Multi-Lingual Plane) of Unicode, but for characters outside this plane pairs of code units encode a single Unicode character. The upshot: .Length
doesn't always return the count of characters, notably not with emoji; e.g., '👋'.length
is 2
Все такие недопустимые символы просто заменяются на � (REPLACEMENT CHARACTER, U+FFFD), чего я не знал. Таким образом, длина 3 имеет смысл. Большое спасибо за ваш ответ и, пожалуйста, извините, что я поздно принял его.
С удовольствием, @RenéNyffenegger; Я рад, что это было полезно.
Связанный: Какая правильная кодировка для файлов PS1