Как стать автором
Обновить

Комментарии 70

Ну, у нас не php, а с++ :) И иногда такие спецэффекты могут быть крайней неприятными. Всё таки BOM-заголовок достаточно удобная вещь.

Честно, никогда не видел плюсов bom.

А с моей точки зрения очень даже удобная штука. Если поставить себя на место какой-нибудь утилиты, то очень удобно просто читать BOM-заголовок, вместо запуска каких-нибудь тяжелых алгоритмов для автодетекта кодировки. Да и какие-нибудь Notepad++ с VSCode сразу правильно определяют кодировку. Почему вы считаете, что у BOM нет плюсов?

Если утилита работает в непредсказуемом окружении, то любая эвристика даст когда-нибудь сбой.

Если окружение предсказуемое, то окружение сообщит утилите, какая кодировка актуальна.

Разумеется эвристика дает сбой. Для этого и нужен BOM, который говорит какая конкретно кодировка используется. В том числе в непредсказуемом окружении.

Например, в GCC она ломается. У разных утилит разные эвристики. У разных файлов разные эвристики. Это наше айти очень сложное, чтобы доверять такие простые вещи (кодировка файла) таким сложным механикам.

А окружение нужно сделать предсказуемым.

Потому что шел 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.

Ну почему же не подключишь. У нас на сервере постоянно гоняется наш собственный анализатор. А если нужны какие-то подсказки анализатору и подобие кастомных правил, то у нас есть пользовательские аннотации в формате JSON. Поправьте, если я неправильно понял ваш комментарий.

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

За что люблю гугловское руководство по стилю: требует {project}_{path}_{filename}_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."

зольный буратино

Звучит почти что как "токсичный" :-)

Зачем такая грустная картинка? Очень жалко жучка ((

Идея была в том, что раз багу в GCC 13 лет, то он наверняка устал там сидеть и всё ломать. Вот и загрустил... Предлагаю поискать других багов, чтобы ему хотя бы не было одиноко.

К сожалению я не внимательно слежу за развитием С++ в последние годы, но приведите пожалуйста ссылку на стандарт где написано, что исходный текст С++ перестал быть 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'ы.

Ну, стандарт не обязывает компиляторы реализовывать вообще какие-нибудь `#pragma`'ы, это правда. Но раз GCC решили его поддержать, то почему бы не поддержать нормально? Количество веселых эффектов от `#pragma` в коде зашкаливает.

Тут такая штука... UTF-8 с BOM на little endian — вне закона стандарта. Вот такая заковыка. Либо UTF-16, либо big endian, либо без BOM. Он не то, чтобы запрещён — он просто не описан в стандарте для этого случая.

Я согласен, что это тупизм, но дура лекс. Сам так попадал в совсем другой ситуации с другим софтом.

GCC 13.2 говорит "ква". Но если убрать "невидимый" символ перед решеткой, то всё становится нормально.

вспомнился один рецепт когда для исключения применения кодавне проекта в перед запуском скриптом отключались некоторые строки в коде....а при просмотре все вроде верно , но при копировании только кода, прихватывались "невидимые" символы...

Вы делаете это неправильно :)

(BOM)#
#pragma once

Мы это прошли ещё в начале 2000-х при переводе PHP скриптов с вин кодировки на utf8.

С тех пор везде убираю BOM. До сих пор периодически ловлю проблемы из-за неё, но уже от других разработчиков и в совершенно другом стеке.

Смотрите, фикс вашей проблемы тривиален. Шланг и гцц и так по умолчанию считают все файлы в кодировке утф8, проблема только с мсвц. Но она решается с помощью одного простого ключика - /utf-8. Все мсвц тоже считает ваши файлы в утф8 и это самое НИКАКИХ бомов даже на пушечный выстрел подпускать нельзя.

Неблагадорите.

+1, BOM не нужен. Для дополнительной надёжности можно дать gcc флажок -finput-charset=UTF-8.

Хорошо. А что вы будете делать, если, предположим, BOM в third-party библиотеке, которую вы не имеете права модифицировать? У вас сборка валится из-за бага в компиляторе. Разве баги не надо фиксить? :)

Я согласен что было бы лучше, если бы GCC корректно обрабатывал данную ситуацию. Но судя по тому как мало комментов в том тикете, проблема затрагивает почти никого. И я даже могу предположить почему. Потому что почти никто не использует BOM. А не испол зует потому что он не нужен. Мало того, юникод-консорциум вообще хочет объявить его discouraged к использованию.

Ну и тем же успехом можно представить что мы наступили на какой-нибудь баг в msvc. Который мы не просто "не имеем права модифицировать", у нас и исходников-то нет. Зарепортили, сочинили workaround, погнали дальше.

Если вы собираете ее из исходников (а иначе как вы получите проблему с бомом) то удаление бома и/или конвертация в утф8 файлов не нарушает вообще никаких лицензий в принципе. Не хотите морочиться с комитами в репе сделайте патч и накатывайте при каждой сборке, симэйк фетч контент делает это из коробки, но это самое вы начинаете придумывать проблемы которых нет ни у вас ни у кого то другого, так просто ради развлечения.

БОМ маст дай.

Аргумент. И всё же я лично считаю, что BOM полезен

Надо научить автора библиотеки нормально сохранять файлы.

Что-то похожее есть в MSVC, если по каким-то причинам в файле оказалось два BOM'а.

Я как-то не понял из текста связь pragma once и BOM-заголовка.

И кто именно из них все ломает и главное почему.

Наличие обоих в предкомпилированном заголовке. Подробнее о проблеме и её причинах вы можете почитать по ссылкам на баг репорты в статье.

Несколько раз ловил проблемы с BOM в linux-скриптах когда он портит #! в начале файла. Такой символ очень трудно найти с помощью стандартных программ работы с текстами, поэтому в виндоузовских редакторах всегда слежу, чтобы BOM был отключен.

Про #pragma - спасибо. В одном проекте долго ломали голову, почему под gcc прагма ломается в паре файлов из сотни. Тогда так и не поняли и начали переходить на инклуд гарды.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий