Пытается ли PowerShell выяснить кодировку скрипта?

Когда я выполняю следующий простой сценарий в PowerShell 7.1, я получаю (правильное) значение 3, независимо от того, является ли кодировка сценария Latin1 или UTF8.

'Bär'.length

Это меня удивляет, потому что у меня сложилось (очевидно неправильное) впечатление, что кодировка по умолчанию в PowerShell 5.1 — UTF16-LE, а в PowerShell 7.1 — UTF-8.

Поскольку оба сценария оценивают выражение как 3, я вынужден заключить, что PowerShell 7.1 применяет некоторый эвристический метод для определения кодировки сценария при его выполнении.

Верен ли мой вывод, и это где-то задокументировано?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
1 123
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Кодировка не имеет отношения к этому случаю: вы вызываете 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 каким-то образом должен определить кодировку файла.

René Nyffenegger 12.12.2020 18:21

Правильно - что может привести к отображению неправильного символа, если Powershell загрузит файл сценария в неправильной кодировке (или если ваша консоль настроена на неправильную кодировку), но это не приведет к тому, что длина будет другой. Количество персонажей остается прежним.

Mitch 12.12.2020 18:27

Я думаю, что без спецификации 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 без спецификации.

    • Поскольку кодовые страницы ANSI используют однобайтовую кодировку с фиксированной шириной, каждый байт, который является частью последовательности байтов 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 14.12.2020 20:59

С удовольствием, @RenéNyffenegger; Я рад, что это было полезно.

mklement0 14.12.2020 21:19

Другие вопросы по теме