Комментарии 70
Честно сказать, в мире php принято сохранять файлы без bom, иначе тоже вылазили спецэффекты.
Ну, у нас не php, а с++ :) И иногда такие спецэффекты могут быть крайней неприятными. Всё таки BOM-заголовок достаточно удобная вещь.
Честно, никогда не видел плюсов bom.
А с моей точки зрения очень даже удобная штука. Если поставить себя на место какой-нибудь утилиты, то очень удобно просто читать BOM-заголовок, вместо запуска каких-нибудь тяжелых алгоритмов для автодетекта кодировки. Да и какие-нибудь Notepad++ с VSCode сразу правильно определяют кодировку. Почему вы считаете, что у BOM нет плюсов?
Если утилита работает в непредсказуемом окружении, то любая эвристика даст когда-нибудь сбой.
Если окружение предсказуемое, то окружение сообщит утилите, какая кодировка актуальна.
Разумеется эвристика дает сбой. Для этого и нужен BOM, который говорит какая конкретно кодировка используется. В том числе в непредсказуемом окружении.
Потому что шел 2024 и все файлы у меня до сих пор без bom. Ибо нафиг оно не надо, если я знаю что это utf8, то я знаю что это utf8 и все ПО что я использую считает что это utf8, а касательно веб - я браузеру сам говорю что вот внутри utf8, используй его. Без всяких там bom.
Не соглашусь.
Если мы знаем, что в файле UTF-8, то BOM просто не нужен.
А если не знаем, что там, то запросто может быть и без BOM, стало быть нам всё равно нужны какие-то эвристики.
А ситуаций, когда случайная %PROGRAMNAME% не понимает BOM было много. И отлаживать их тяжело.
Разумно. Но всё же, вы предлагаете просто полагаться на то, что файлы в utf-8. А если там всё таки utf-16? Big endian или little endian? Ну вот возникла у вас необходимость один/несколько файлов проекта сохранить в таких кодировках. Как раз удобно по BOM-заголовку однозначно считывать эту информацию. Просто полагаться на то, что "повезет" и файл окажется в utf-8 - сомнительная идея, на мой взгляд.
Дак опять же, тут ситуация, что мы "знаем", в какой кодировке сохраняли. Можно конечно такую информацию запихать и в BOM, но это не очень удобно, если полученные файлы надо обрабатывать чем-то, кроме своего кода.
И слабо представляю ситуацию, когда нужен UTF-16, если честно.
Windows ini и reg-файлы в этой кодировке. Регулярно попдаются утилиты которые неверно определяют кодировку.
Там оно по историческим причинам, legacy так сказать. И для сохранения обратной совместимости мы конечно его переконвертировать не будем.
Вопрос вот какой: для чего нам в нашем проекте может потребоваться сохранять данные в UTF-16? Имеются в виду конечно те данные, которые "внутри" проекта (понятно, что при интеграции с чем-то, где используется UTF-16, нам придётся его использовать, но от этого конечно не денешься никуда).
Касаемо же утилит, неверно определяющих кодировку - а зачем её определять?
Опять же, либо мы знаем кодировку полученных данных (по заголовкам запросов-ответов или по какому-то соглашению вида "все наши файлы храним в UTF-8"), либо не знаем. И во втором случае BOM опять же может как быть, так и нет (т.е. ситуацию "прилетело нечто без BOM" обрабатывать придётся).
BOM поможет в ситуации "можем иметь на входе разный юникод, но обязательно юникод и обязательно с BOM". Но мне такая ситуация кажется крайне маловероятной.
А если там всё таки utf-16?
git blame, а дальше руками. Если не доходит, ногами
А если там всё таки utf-16?
В таких файлах в начале должны быть байты FFFE, разве нет?
Давно, лет 17 назад, я сидел на Windows.. Понадобилось переключить что-то на линуксовом сервере (SIP-прокси Kamailio). Я скачал файлик по ssh, исправил и закачал обратно.. Рестартовал сервис и забыл. Через несколько часов в другой команде кто-то обнаружил, что сервис не работает. Несколько человек полдня пытались понять что случилось и почему - файл внешне выглядел корректно. Потом самый вежливый член команды объяснил мне мою ошибку.
Уже лет через 5 я сам потратил целый день, выясняя почему какой-то Java-сервис не читает свой xml-конфиг. Выяснилось что в одной строке вместо отступов пробелами затесался один символ табуляции..
Да, самые раздражающие и труднонаходимые ошибки обычно оказываются на деле очень простыми, лежащими на поверхности. Древний мем про забытую точку с запятой не просто так появился, хоть сейчас и не актуален :)
Была пол года назад похожая проблема, но у нас вместо обычного пробела затесался "широкий пробел", который текстовый редактор отображал так же как и обычный пробел 😅
Я как-то в конце строки в одном из текстовых полей БД нашёл пробел нулевой ширины. Очевидно, что из-за него не проходило сравнение строк.
Наверное было "весело" такое искать
Веселее была буква Ё, которая не Ё, а какой-то греческий символ с точками. Долго гадали откуда в базе (в фамилии) взялся этот символ. Даже пригласили человека со смартфоном, который вбивал данные в базу.
Так вот: на определённой андроид-клавиатуре, если зажать букву Е, то подставлялась не Ё, а вот этот непонятный символ. И самая мякотка: в базе данных используются немного разные кодировки (какой-то недо-юникод и другой недо-юникод), так этот символ сохраняется РАЗНЫМИ кодами.
Вот такой кусочек кода живёт сейчас в проде:
--LATIN CAPITAL LETTER E WITH DIAERESIS
:fio := replace(:fio, chr(50059), 'Ё');
Вам привет от владельцев автомобилей CITROЁN (ну и шкоды за одно). Вот эта Ё по идее французская, хотя и выглядит как русская. Но может быть и русской. Может быть умляутом ( буква Е плюс символ "две точки"). И работа с этим вот всем совершенно непредсказуема. Может отличаться в одном и том же софте (СУБД например) из-за выбранного collation.
Как итог, купить ОСАГО или там КАСКО может быть весьма нетривиальным квестом.
Я уже несколько лет назад узнал про существование этих андроид-клавиатур, вводящих символ "LATIN CAPITAL LETTER E WITH DIAERESIS" вместо русского "Ё". А получилось это так - у меня используются формы для заполнения анкет, а потом я сверяю ФИО со своей базой. И да, я знаю, что буква "Ё" то ли бывает, то ли не бывает, и всегда при сверке введённых данных с базой меняю "Ё" на "Е". И когда это перестало работать, я долго разбирался, потом пристыдил человека, как он смог так странно ввести фамилию, он сказал - "да просто ввёл, ничего необычного", а потом такие случаи стали повторяться...
случаи стали повторяться...
Только и осталось сказать — У, Ё!
Время от времени банковские служащие отказывают в получении денежных перевод по ФИО - буквы не совпадают с теми что в личных документах.
Различные Артёмы, Семёны и Алёны посылают таким как вы, любителям заменить буквы, свои лучики поноса :)
Да, статические анализаторы вещь крайне приятная, жаль, только идею, как бекенд для этих проверок не подключишь в ci.
Самое милое, что когда пытаешься запустить файл с CR+LF вручную в шелле, то получаешь ответ, что такого файла нет. Да как же его нет, когда вот он. Все, начиная с ls
, подтвердят, что файл есть.
Я не разбирался, чей это косяк (шелла?), но это сам по себе косяк - когда пользователю вместо вменяемого сообщения об ошибке выдаётся какой-то бред.
А вот в PowerShell ещё хуже, содержимое строки в программе зависит от того, в какой кодировке записан скрипт
А в PVS-Studio есть диагностика для отлова таких вот нюансов?
На данный момент нет, да и не уверен, что это нужно. Да, мы можем специально для GCC выдавать предупреждения, если в заголовочных файлах есть BOM-заголовок. Но ответственен ли статический анализ за ошибки, допущенные из-за таких нюансов? Мне кажется нет, ведь это баг компилятора.
Я понимаю, что ответственность тут на разработчиках компилятора. Но для статического анализатора мне кажется что это был бы плюс - отлов таких вот нюансов, конечно при условии что эта фича не слишком сложно реализуема. Тут как говорится: вам шашечки или ехать?
Недавно ловил проблему со связкой msvc + unity build + forward declaration. Когда в одной единице трансляции тип был через forward declaration указан как struct, а в другой - это был класс. И студия просто выдавала либо internal compiler error либо out of memory. Очень сложно было отлаживать, хотя тот же clang смог показать соответствующее предупреждение.
Звучит здраво. Вопрос только в потребности пользователей в такой диагностике. Я закину эту идею на подумать и, возможно, мы в скором времени порадуем пользователей новыми диагностиками на такие темы.
Спасибо. Тут ещё неочевидный вопрос возникнет - а как отлавливать обновления компиляторов? Это получается что для такой диагностики надо написать юнит тест, где проверяется что компилятор гарантированно натыкается на баг, и как только компилятор обновится - тест покажет что в диагностике надо указать верхнюю версию компилятора.
Так что я понимаю, что абстрактно ловить ошибки компиляторов и ругаться на них - та ещё проблема.
Да, искать ошибки в коде в принципе нетривиальная задача. Конечно, если эти ошибки зависят от версий стандартов/компиляторов/чего-то еще, то сложность только увеличивается. Если вам интересно, как примерно это работает, то у нас есть относительно старая статья про наши технологии. А здесь статья посвежее про то, как у нас работает межмодульный анализ.
не уверен, что это нужно
famous last words.
QtCreator выводит предупреждение, если UTF-8 c BOM. (Как на big endian - не знаю). В конце концов, это своего рода undefined behaviour. В стандарте UTF-8 этот BOM не описан.
То есть та моя идея плейнтекстового редактора, в котором можно настроить три разные кодировки для строк, заканчивающихся на CR, LF и CR/LF — это ещё не дно садизма…
Упомянутые в статье include guard во многом хуже pragma once, и вовсе не только из-за многословности. Встречал самолично, как два различных файла в разных сторонних библиотеках объявляли один и тот же include guard макрос - с каким-то простым именем вроде CONFIG_H. Весело было разбираться, почему содержимое включаемого файла не видно, пока не докопался до этой проблемы.
Похоже автор сам себе зольный буратино.
Решили сменить кодировку и для облегчения работы добавили невидимый префикс- BOM, который вообще-то не стандартный и полу/нерабочий.
Если надо было отследить файлы которые были изменены- надо было по примеру Linux скриптов добавить комментарий : // converted-to-utf-8
Тогда ваши программы смогут определить был ли файл сконцентрирован или нет.
Все прозрачно и понятно.
вообще-то не стандартный
https://datatracker.ietf.org/doc/html/rfc3629#section-6
Раз в стандарте описан, значит, стандартный.
Или речь шла про стандарт С++23? Он, да, предписывает игнорировать BOM: "If the first translation character is u+feff byte order mark, it is deleted."
зольный буратино
Звучит почти что как "токсичный" :-)
Зачем такая грустная картинка? Очень жалко жучка ((
К сожалению я не внимательно слежу за развитием С++ в последние годы, но приведите пожалуйста ссылку на стандарт где написано, что исходный текст С++ перестал быть ASCII и стал каким-то юникодом.
Это ещё Пётр Первый С++98 ввёл костыльно, С++11 полноценно. Я про Юникодные литералы. Юникодные комментарии тоже введены тогда же: согласитесь, было бы странно, если закомментил строку с литералом, а оно всё перестало парситься от этого. А в символах Юникод не поддерживается, это вам не Питон.
MSVC поддерживает кириллицу в именах, что приводит иногда к конфузам, когда "с" и "c" перепутали.
Литералы тут не у дел, в одном файле можно смешать хоть все 4 типа (про LE/BE в UTF-16/32 промолчу). Никакой BOM тут не поможет. Более того, стандарт С++23 раздел 5.2.1 требует распознавания файлов UTF-8 независимо от их содержания, а 5.2.2 впридачу игнорировать BOM с порядком байтов в юникоде.
Юникод в идентификаторах поддерживается, раздел 5.10
Но вот если ваш продукт кроссплатформенный, собирающийся под разными компиляторами, тогда у вас беда и печаль
Нет, тогда в коде не может быть ни одной #pragma
'ы.
Тут такая штука... UTF-8 с BOM на little endian — вне закона стандарта. Вот такая заковыка. Либо UTF-16, либо big endian, либо без BOM. Он не то, чтобы запрещён — он просто не описан в стандарте для этого случая.
Я согласен, что это тупизм, но дура лекс. Сам так попадал в совсем другой ситуации с другим софтом.
В качестве предположения, а такой код скомпилируется?
#
#pragma once
struct MyClass
{
static int return_zero() noexcept;
};
GCC 13.2 говорит "ква". Но если убрать "невидимый" символ перед решеткой, то всё становится нормально.
Мы это прошли ещё в начале 2000-х при переводе PHP скриптов с вин кодировки на utf8.
С тех пор везде убираю BOM. До сих пор периодически ловлю проблемы из-за неё, но уже от других разработчиков и в совершенно другом стеке.
Смотрите, фикс вашей проблемы тривиален. Шланг и гцц и так по умолчанию считают все файлы в кодировке утф8, проблема только с мсвц. Но она решается с помощью одного простого ключика - /utf-8. Все мсвц тоже считает ваши файлы в утф8 и это самое НИКАКИХ бомов даже на пушечный выстрел подпускать нельзя.
Неблагадорите.
+1, BOM не нужен. Для дополнительной надёжности можно дать gcc флажок -finput-charset=UTF-8
.
Хорошо. А что вы будете делать, если, предположим, BOM в third-party библиотеке, которую вы не имеете права модифицировать? У вас сборка валится из-за бага в компиляторе. Разве баги не надо фиксить? :)
Я согласен что было бы лучше, если бы GCC корректно обрабатывал данную ситуацию. Но судя по тому как мало комментов в том тикете, проблема затрагивает почти никого. И я даже могу предположить почему. Потому что почти никто не использует BOM. А не испол зует потому что он не нужен. Мало того, юникод-консорциум вообще хочет объявить его discouraged к использованию.
Ну и тем же успехом можно представить что мы наступили на какой-нибудь баг в msvc. Который мы не просто "не имеем права модифицировать", у нас и исходников-то нет. Зарепортили, сочинили workaround, погнали дальше.
Если вы собираете ее из исходников (а иначе как вы получите проблему с бомом) то удаление бома и/или конвертация в утф8 файлов не нарушает вообще никаких лицензий в принципе. Не хотите морочиться с комитами в репе сделайте патч и накатывайте при каждой сборке, симэйк фетч контент делает это из коробки, но это самое вы начинаете придумывать проблемы которых нет ни у вас ни у кого то другого, так просто ради развлечения.
БОМ маст дай.
Надо научить автора библиотеки нормально сохранять файлы.
Что-то похожее есть в MSVC, если по каким-то причинам в файле оказалось два BOM'а.
Я как-то не понял из текста связь pragma once и BOM-заголовка.
И кто именно из них все ломает и главное почему.
Несколько раз ловил проблемы с BOM в linux-скриптах когда он портит #! в начале файла. Такой символ очень трудно найти с помощью стандартных программ работы с текстами, поэтому в виндоузовских редакторах всегда слежу, чтобы BOM был отключен.
Про #pragma - спасибо. В одном проекте долго ломали голову, почему под gcc прагма ломается в паре файлов из сотни. Тогда так и не поняли и начали переходить на инклуд гарды.
Насекомое 13 лет сидит в вашем компиляторе и не собирается оттуда вылезать