Вопрос или проблема
Этот вопрос тесно связан с: Как вставить текст перед первой строкой файла?. Я специально сделал заголовок похожим на этот вопрос, чтобы подчеркнуть это.
За исключением целевого файла, который имеет 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 важна для обеспечения совместимости различных систем и приложений.