Скрипт PowerShell не сохраняет кодировку исходного файла - PullRequest
1 голос
/ 20 января 2020

У меня есть исходный csv-файл, который довольно большой, и чтобы иметь возможность работать с ним более эффективно, я решил разбить его на более мелкие куски файлов. Для этого я выполняю следующий скрипт:

Get-Content C:\Users\me\Desktop\savedDataframe.csv -ReadCount 250000 | %{$i++; $_ | Out-File C:\Users\me\Desktop\Processed\splitfile_$i.csv}

Как видите, это csv-файлы, которые содержат данные alphanumeri c. Итак, у меня есть проблема со строками, похожими на эту:

H ä meenkatu 33

В целевом файле это выглядит так:

Hämeenkatu 33

Я пытался определить кодировку исходного файла, и это UTF-8 (как описано здесь ). Мне действительно интересно, почему это так запутано в цели. Я также попробовал следующее, чтобы явно сказать, что я хочу, чтобы кодировка была UTF8, но безуспешно:

Get-Content C:\Users\me\Desktop\savedDataframe.csv -ReadCount 250000 | %{$i++; $_ | Out-File -Encoding "UTF8" C:\Users\me\Desktop\Processed\splitfile_$i.csv}

Я использую Windows машину, работающую Windows 10.

Ответы [ 2 ]

1 голос
/ 21 января 2020

ответ js2010 обеспечивает эффективное решение; позвольте мне дополнить его справочной информацией (краткое описание рассматриваемого дела находится внизу):

По существу, PowerShell никогда сохраняет кодировку символов входного файла [текст] на выходе :

  • при чтение , содержимое файла декодировано в. NET строки (которые являются внутренними единицами кода UTF-16):

    • Файлы с спецификацией для следующих кодировок всегда правильно распознаются (идентификаторы, распознаваемые параметром -Encoding командлетов PowerShell в скобках):

      • UTF-8 (UTF8) - info
      • UTF-16LE (Unicode) / UTF-16BE (BigEndianUnicode) - info
      • UTF-32LE (UTF32) / UTF-32BE (BigEndianUTF32) - info
      • Обратите внимание на отсутствие UTF-7 , которое, однако, на практике редко используется в качестве кодировки.
    • Без Спецификация , по умолчанию предполагается кодировка :

      • PowerShell [Core] v6 + похвально предполагает UTF-8 .
      • Устаревшая Windows PowerShell (PowerShell до v5.1) предполагает кодирование ANSI , то есть кодовую страницу, определяемую языковой версией устаревшей системы; например, Windows -1252 в системах US-Engli sh.
    • Параметр -Encoding командлетов для чтения файлов позволяет вам явно указывает исходную кодировку , но учтите, что наличие (поддерживаемой) спецификации переопределяет this - сведения о поддерживаемых кодировках см. Ниже.

  • При записи ,. NET строки кодируются на основе кодировки по умолчанию , если только кодировка явно не указана с помощью -Encoding (строки. NET, созданные при чтении, не содержат никакой информации о кодировке исходного входного файла, поэтому ее нельзя сохранить):

    • PowerShell [Core] v6 + похвально использует без спецификации UTF-8 .

    • Наследие Windows PowerShell (PowerShell до v5.1), к сожалению использует различные кодировки по умолчанию, в зависимости от конкретного использования c командлета / оператора d .

      • Примечательно, что Set-Content по умолчанию соответствует ANSI (для чтения) и Out-File / > по умолчанию UTF-16LE .

      • Как отмечено в ответе js2010 с использованием -Encoding UTF8 в Windows PowerShell неизменно создает файлы с спецификацией , что может быть проблематичным c для файлов, считываемых инструментами на Unix платформы / инструменты с Unix наследием, которые часто не оснащены для работы с такой спецификацией.

        • См. ответы на этот вопрос , чтобы узнать, как создать без спецификации. Файлы UTF-8 в Windows PowerShell.
    • Как и при чтении, параметр -Encoding записи в файл Командлеты позволяют явно указывать выходную кодировку :

      • Обратите внимание, что в PowerShell [Core] v6 +, в дополнение к по умолчанию для спецификации без UTF-8, -Encoding UTF8 тоже относится к т вариант без спецификации (в отличие от Windows PowerShell), и там вы должны использовать -Encoding UTF8BOM для создания файла с спецификацией.

      • Любопытно Начиная с PowerShell [Core] v7.0, для активной кодовой страницы ANSI системы есть значение no -Encoding, то есть для Windows PowerShell по умолчанию (в Windows PowerShell, -Encoding Default явно запрашивать кодировку ANSI, но в PowerShell [Core] это относится к UTF-8 без спецификации). Это проблемное c упущение обсуждается в этом выпуске GitHub . Напротив, нацеливание на активную кодовую страницу OEM с -Encoding OEM все еще работает.

      • Для создания файлов UTF-32BE Windows PowerShell требуется идентификатор BigEndianUtf32; из-за ошибки в PowerShell [Core] начиная с версии 7.0 этот идентификатор не поддерживается, но вместо него можно использовать UTF-32BE.

      • Windows PowerShell ограничен теми кодировками, которые перечислены в перечислении Microsoft.PowerShell.Commands.FileSystemCmdletProviderEncoding, но PowerShell [Core] позволяет передавать любой из поддерживаемых . NET кодирование в -Encoding параметр либо по номеру кодовой страницы (например, 1252), либо по имени кодировки (например, windows-1252); [Text.Encoding]::GetEncodings().CodePage и [Text.Encoding]::GetEncodings().Name перечисляют их в принципе, но учтите, что из-за отсутствия. NET поддержки Core API с версии v7.0 это перечисление перечисляет только небольшое подмножество фактически поддерживаемых кодировок; выполнение этих команд в Windows PowerShell покажет их все.

      • Вы можете создавать файлы UTF-7 (UTF7), но они не будут иметь спецификацию; даже входные файлы, у которых они есть, не распознаются автоматически при чтении, поэтому указание -Encoding UTF7 равно всегда , необходимое для чтения файлов UTF-7.

Вкратце:

  • В PowerShell необходимо знать кодировку входного файла, чтобы соответствовать этому кодирование при записи и укажите, что кодировка явно через параметр -Encoding (если он отличается от значения по умолчанию).

  • Get-Content (без -Encoding) обеспечивает нет информация о том, какую кодировку он обнаружил через спецификацию или какую кодировку он предположил в отсутствие спецификации.

  • При необходимости вы можете выполнить собственный анализ начальные байты текстового файла для поиска спецификации, но учтите, что при отсутствии таковой вам придется полагаться на эвристику до выводить кодировку - то есть вы может сделать разумное предположение, но вы не можете быть уверены.

Также обратите внимание, что Пау В rShell, начиная с v7, принципиально отсутствует поддержка прохождения необработанных потоков байтов через конвейер - см. этот ответ .


Ваш конкретный случай :

Ваша проблема заключалась в том, что ваш входной файл был в кодировке UTF-8, но не имел спецификации (что на самом деле предпочтительнее для самой широкой совместимости).

Поскольку вы используете Windows PowerShell , который неправильно интерпретирует такие файлы, как кодированные в ANSI, вы должны указать ему прочитать файл как UTF-8 с -Encoding Utf8.

Как указано, при записи -Encoding Utf8 неизбежно создает файл с спецификацией в Windows PowerShell; если это вызывает озабоченность, используйте платформу. NET напрямую для создания файлов без спецификации, как показано в ответах на этот вопрос .

Обратите внимание, что вы бы у нет проблемы с исходной командой в PowerShell [Core] v6 + - по умолчанию используется UTF-8 без спецификации как для чтения, так и для записи во всех командлетах.

Одно только это разумное стандартизированное значение по умолчанию является хорошей причиной для рассмотрения перехода на PowerShell v7.0, который призван стать превосходной заменой устаревшей Windows PowerShell.

1 голос
/ 20 января 2020

Есть ли во входном файле бомба? Попробуйте get-content -encoding utf8. Исходный файл по умолчанию имеет значение utf16le или что windows и PowerShell называют «Unicode».

Get-Content -encoding utf8 C:\Users\me\Desktop\savedDataframe.csv -ReadCount 250000 | 
  %{$i++; $_ | 
  Out-File -encoding utf8 C:\Users\me\Desktop\Processed\splitfile_$i.csv}

Выходной файл будет иметь bom, если вы не используете PowerShell 6 или 7.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...