Как вставить текст перед первым рядом файла UTF-8 с BOM

Вопрос или проблема

Этот вопрос тесно связан с: Как вставить текст перед первой строкой файла?. Я специально сделал заголовок похожим на этот вопрос, чтобы подчеркнуть это.

За исключением целевого файла, который имеет UTF-8 с BOM.

Итак, я хочу добавить первую строку в файл, который имеет байты UTF-8 BOM в начале (0xef 0xbb 0xbf символы). По крайней мере, файлы UTF-8 с BOM, которые у меня есть, начинаются с этого.

Если я просто продолжу и воспользуюсь решениями из связанного вопроса,

sed "1i Моя первая строка теперь такая."

Я получу (в VSCode в моем случае) что-то вроде

Моя первая строка теперь такая.
?Первая строка была такой
Вторая строка и так далее

Символ ? во второй строке представляет собой символ UTF-8, выражающий что-то непечатаемое.

Еще одно следствие, как следовало ожидать, заключается в том, что файл больше не открывается как UTF-8 с BOM, и теперь мы полагаемся на функции текстового редактора, чтобы “угадывать” его кодировку. Мы определили шаблон в нашем проекте, чтобы иметь файлы с BOM, чтобы гарантировать, что все в одной и той же кодировке.

Как мне сохранить заголовок BOM в файле, добавляя текст?

Из того же связанного вопроса один ответ открывает дверь к решению здесь: https://unix.stackexchange.com/a/455250/413756

Затем с небольшими манипуляциями с регулярными выражениями и группами выбора, я придумал это выражение:

sed -Ei "1s/^(|\xef\xbb\xbf)/\1Моя первая строка теперь такая.\n/" file.txt

И я получил желаемый результат, сохраняя заголовок UTF-8 BOM в файле и даже делая замену в файлах, которые в конечном итоге не соответствуют заголовку.

Если вы уверены, что все файлы имеют BOM, тогда этого должно быть достаточно:

sed -Ei "1s/^(\xef\xbb\xbf)/\1Моя первая строка теперь такая.\n/" file.txt

Обратите внимание, что это захватывает конкретно файлы UTF-8 BOM с точно таким же заголовком, который у меня здесь. Я полагаю, что разные последовательности байтов могут встречаться в разных форматах, что зависит от каждого случая — и, вероятно, возможно с вышеупомянутым регулярным выражением (просто добавьте/замените |\xXX\xYY\xZZ или соответствующее совпадение в группу выбора).

Также обратите внимание, что в обеих заменах я предполагаю, что окончание строк в файлах — это просто \n (unix LF), а не \r\n (windows CRLF).

Вышеуказанный синтаксис работал на GNU Sed (Cygwin и, вероятно, большинство дистрибутивов Linux).

UTF-8 BOM очень не Unix, скорее всего, он пришел из мира Microsoft. Тогда окончание строки также, вероятно, будет CRLF, а не LF, и могут быть другие идиосинкразии Microsoft, с которыми нужно иметь дело. Если вы хотите обрабатывать их с помощью Unix/GNU инструментов, вам, вероятно, лучше сделать:

dos2unix -- "$file" && sed -i -e '1i\
новая строка' -- "$file" && unix2dos -m -- "$file"

Для редактирования на месте (здесь используется GNU стиль -i редактирования на месте) или:

{
  echo новая строка
  <"$file" dos2unix
} | unix2dos -m > "$newfile"

Чтобы обрабатывать на лету (здесь без необходимости в sed) и сохранить результат (обратно в формате Microsoft с BOM) в новом файле.

Ответ или решение

Для вставки текста перед первой строкой файла с кодировкой UTF-8 и BOM (Byte Order Mark) мы можем использовать различные подходы, чтобы гарантировать, что BOM будет сохранён, а файл не потеряет свою корректную кодировку.

Проблема

Файлы с BOM начинаются с определённых байтов (0xef 0xbb 0xbf), и если мы используем стандартные команды для вставки строк, это может привести к нарушению структуры файла, в частности, к появлению недопустимых символов. Например, если мы попытаемся использовать команду sed:

sed "1i My First line is now this." file.txt

Это может привести к тому, что файл перестанет быть корректным UTF-8 с BOM, так как в результате может появиться недопустимый символ (например, ?), что делает файл не читаемым для некоторых текстовых редакторов.

Решение

Вариант 1: Использование sed

Одним из способов корректно вставить строку с сохранением BOM является использование sed с регулярными выражениями, которые учитывают наличие BOM:

sed -Ei "1s/^(|\xef\xbb\xbf)/\1My First line is now this.\n/" file.txt

Этот подход позволяет вставить строку, не нарушая BOM. В данном случае регулярное выражение ищет начало строки, включая возможный BOM, и перед вставкой текста мы уберегаем BOM от изменения.

Если вы уверены, что все файлы имеют BOM, вы можете использовать следующую более специфическую команду:

sed -Ei "1s/^(\xef\xbb\xbf)/\1My First line is now this.\n/" file.txt

Эта команда вставляет строку перед первой строкой файла, лишь если файл точно начинается с BOM.

Примечание

Этот синтаксис работает в GNU sed, который доступен на большинстве дистрибутивов Linux, а также в Cygwin.

Вариант 2: Обработка файлов Windows (CRLF)

Если ваши файлы могут быть в формате Windows (с символами окончания строк CRLF), вам следует использовать инструмент dos2unix, чтобы сначала преобразовать файл, затем выполнить вставку с помощью sed, и затем вернуть файл в исходный формат. Пример:

dos2unix -- "$file" && \
sed -i -e '1i\
My First line is now this.' -- "$file" && \
unix2dos -m -- "$file"

Этот подход гарантирует, что вы правильно обработаете строки с окончанием CRLF, а при этом сохранив BOM.

Альтернативный способ

Если вы не хотите использовать sed, вы можете выполнить вставку строк на лету:

{
  echo My First line is now this.
  <"$file" dos2unix
} | unix2dos -m > "$newfile"

Этот способ позволяет считать оригинальный файл, добавив новую строку в начало и сохраняя BOM в новом выходном файле.

Заключение

Существует множество подходов для добавления строки перед первой строкой в файле UTF-8 с BOM. Выбор метода может зависеть от вашего окружения и формата исходного файла. Убедитесь, что вы понимаете структуру вашего файла, чтобы избежать возможных проблем с кодировкой в будущем. Правильная работа с BOM важна для обеспечения совместимости различных систем и приложений.

Оцените материал
Добавить комментарий

Капча загружается...