Комментарии 81
А можно ссылочку, где A-API признается устаревшим самими авторами? Залез в MSDN - не вижу ничего про deprecated в CreateFileA, например...
Вы не видите потому что это не правда
А я уж подумал все, придется мне теперь только с W-версиями работать ;-)
Просто если хочешь многоязыковую поддержку - забудь про них и все. Они не устарели, но большинство A-функции - это тупо прослойки между W-функциями и системой (не все). А это снижение производительности и узкая заточенность под твою страну. Если ты живешь в 2000-м - милости просим, используй A-функции, если ты давно переехал в Unicode-мир, то незачем.
Ну тут смотреть надо... Так-то все эти A- и W- тоже прокладка между пользовательским кодом и всякими там Nt- и Zw- APIшками ;-)
Немного не соглашусь. Современный мир — это кросс-платформенные приложения и доминирование UTF-8.
И лично я не вижу принципиальной разницы между конвертированием UTF-8 в UTF-16 для вызова W-функций и вызовов A-функций с конвертацией внутри системной библиотеки.
Я бы, наоборот, похоронил кодовые страницы и сделал UTF-8 единственной возможной кодировкой для A-функций, вдохнув тем самым в них новую жизнь. А W-функции оставил исключительно для Windows-специфичных задач.
В случае с UTF-8 есть определённые проблемы при работе с символами, т.к. 1 символ может быть как 1 байт, так и 4. Могут быть проблемы с производительностью при анализе строки
А c UTF-16 таких проблем, что, нет?
Нет, там все символы 2 байта. А в UTF-8 нельзя рассчитывать, что следующий символ имеет точно такой же размер, что и предыдущий
Все символы по два байта в UCS2, но не в UTF16, где тоже существуют последовательности из нескольких код-поинтов.
Вы что-то путаете, ucs-2 старая схема, которая сейчас не используется. utf16 и ucs-2 идентичны для 65000 символов. 65000 вполне хватает для большинства кодировок. И по сути нет необходимости в расширении. У utf-8 проблемы начинаются сразу, например, с русских символов.
Во-первых, использование символов с кодами, большими 0xFFFF, уже давно стало повсеместным — это эмодзи. Во-вторых, 1 символ ≠ 1 кодпоинт.
И никаких проблем с UTF-8 нет, вы просто работаете с байтовым представлением строки. А в случае с UTF-16 — с двухбайтовым. Но в обоих случаях напрямую работать с символами вы не можете.
В отрыве от правильных слов о разнице между UCS-2 и UTF-16, хочется сказать, что решение засунуть эмодзи в юникод я считаю абсолютно идиотской инициативой.
Тоже, но в меньшей степени относится к включению туда всяких мёртвых вариантов письменности, которые в последний раз использовались в 12 веке, а сейчас интересуют только археологов и палеолингвистов.
Само ограничение в 64К символов и возможность умещаться в 2 байта на символ и иметь прозрачное преобразование индекса символа в смещение в байтах и возможность сканировать строку, просто перебирая 16-битные слова — она очень соблазнительная и дорогого стоит.
Значит я что-то путаю)
Суррогатные пары...
Об этом на сайте самих M$ много и хорошо написано, начинать можно от сюда: https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms904394(v=msdn.10)?redirectedfrom=MSDN
Фишка в том, что СЕ поддерживала только W-функции и не имела слоя совместимости с А-функциями.
65000 вполне хватает для большинства кодировок
ответ с той же страницы
Surrogates provide additional character support for the languages that need more than the 65,536 characters in the 16-bit Unicode code space. For example, the Chinese speaking community alone uses over 55,000 characters.
свыше 2 байт практически ненужные символы (типа вымерших языков)
Эти проблемы возникают при работе с любым юникодом. Потому что unicode codepoint (т.е. то, что программисты называют multibyte character) это ни разу не обязательно один символ на экране. Потому что есть combining characters и лигатуры. Последнее вообще делает количество символов в конкретной строке шрифтозависимым.
При этом в огромном числе задач нам вообще плевать на юникод. Имя файла — это последовательность байт, а сколько там символов или какого цвета какашка-эмодзи, нас вообще не волнует.
В идеале да, неплохо бы сделать A-функции работающими с UTF-8, хотя бы в рамках одного процесса, и забыть о проблемах.
А вот в реальности вы русские буквы не можете прочитать из консоли через ReadFile когда кодовую страницу 65001 используете, так что костыли будут в программах ещё долго.
Просто если хочешь многоязыковую поддержку - забудь про них и все.
Как бы в обозримом будущем не пришлось забыть как раз-таки про W функции:
https://docs.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page
Наконец-то может свершиться, и мейнстримом в Windows станет UTF-8, как везде, а не эта убогая система с A/W функциями.
Оно не устарело, просто есть нюанс, что A-функции вызывались напрямую в системах 9х, а вот W-функции в этих же операционках были прослойками, транслирующими unicode строки в однобайтные и вызывающими A-варианты.
В NT-системах (а все M$ операционки, начиная с 2000, XP и далее - это ядро NT) всё наоборот, W-функции являются основными и родными для ядра, а А-функции лишь прослойки для обратной совместимости в которых однобайтная строка будет преобразована в wide-вариант с применением настроек системы "по умолчанию". Поэтому и существует настройка в недрах системы как "использовать кодировку для не unicode-программ", что порой приводит к неоднозначным результатам, когда преобразование выполняется не как ожидает программист, при этом не во всех системах.
Читал читал, но так и не понял что это за такая стандартная библиотека которая не поддерживает Unicode. Можете пояснить что под этим подразумевается?
Вот эта библиотека:
https://en.cppreference.com/w/c/header
Эта библиотека кросс-платформенная и расчитана на работу с POSIX-совместимыми операционными системами. Но Windows таковой не является, из-за чего и вылезают описанные в статье спецэффекты.
Всё дело в том, что в POSIX-совместимых операционных системах кодировки для имён файлов не используются. Имя файла — это произвольная последовательность байт, которая совершенно не обязательно должна быть валидной UTF-8 строкой, главное чтобы не было запрещённых символов. А работа с консолью — это работа с байтовыми потоками.
Соответственно, поддержки Unicode в стандартной библиотеке нет просто потому, что эта поддержка не нужна. А использование UTF-8 в Unix-подобных операционных системах — это не более, чем негласное соглашение.
Ну а Windows пошла своим путём. Придумала файловую систему, где имена файлов — это UCS2 (а затем и UTF-16), а однобайтовый API стал зависим от кодовой страницы. Полноценный переход однобайтового API на UTF-8 начался лишь недавно.
Недавно наткнулся на то что std::map в Visual Studio C++ крайне медленный. Причём в C# он работает прекрасно.
Заюзай std::unordered_map )
unordered_map тоже не идеален. Для интереса сравнил с гугловским dense_hash_map. Вставка 10 миллионов чисел, затем их же поиск. Результаты на стареньком ноутбуке с Core i5-3317U 1.7GHz:
unordered_set - 7.15 секунд
dense_hash_map - 1.75 секунды
Э-э-э, а где вы в C# нашли std::map?
Bы серьёзно? std::basic_string<T> ... std::basic_string_view<T>. А UCS-2 в отличии от UTF-8 было просто удобнее читать и считать символы/байты (wchar_t / 2) - не надо анализировать токены.
Вот уже лет 5, с тех пор как мы решили перевели под Windows свои консольные приложения в UTF-8, всё что нужно сделать, это компилировать в режиме wide chars и поставить:
::SetConsoleOutputCP(CP_UTF8)
После этого просто выводим строки std::string в которых лежит UTF-8 напрямую в std::cout и нет никаких проблем. std::string также всё равно какая там кодировка, главное не работать с код-юнитами (char), как с символами.Несколько лет назад мы делали проект для израильтян. В GUI всё было хорошо, а вот в консоли нет. Как обычно, если что-то не работает -- читай документацию, оказалось тогда консоль Windows не работала с языками, где пишут справа налево, по определению. Теперь что-нибудь изменилось?
А теперь попробуйте в этой кодировке прочитать из консоли что-нибудь.
Правда, эта настройка имеет отношение лишь к выводу данных. То есть — программы могут писать в консоль, пользуясь UTF-8, но не читать из консоли. К сожалению, возможность чтения UTF-8-текстов из консоли всё ещё не работает.Вопрос только в контексте о том, зачем нужна сторонняя библиотека.
Тут, кстати, еще нужно заметить, что не работает не сам консольный ввод в UTF-8, а именно ввод с клавиатуры. Перенаправления из файла или пайпа прекрасно работают.
А библиотека эту проблему не решает? Тогда она и правда не нужна.
Если вам нужно читать Unicode-данные из консоли в интерактивном режиме — вам не остаётся ничего кроме обхода библиотеки времени выполнения C, так как вышеописанный механизм всё ещё неработоспособен.Т.е. звучит это так, что с библиотекой, что без библиотеки, мы имеем один и тот же результат. А заголовок «Капля здравого смысла...» ??♂️.
putchar('\n');
printf("\n");
пишет в stdout по 2 байта (0x0D, 0x0A), с библиотекой — только 0x0A.
Код, написанный в старом олдовом стиле с обработкой потоков через getchar/putchar, от этого сильно зависим.
Не знаю как работает рантайм C библиотеки, а могу лишь догадываться, что \r\n на \n меняет именно он, раз на низком уровне мы всегда работаем с потоком байт. Хотелось бы конечно увидеть весть пример с комментариями, что вы хотите получить и что выходит на самом деле. На мой взгляд, если уж под windows принято заменять \n на \r\n, то все функции чтения/записи строк тогда уж должны это делать единообразно. Опять же это никак не решает задачу передачи таких файлов с одной систему на другую. На мой взгляд проблема гораздо глубже чем ввод/вывод в консоль.
#include <iostream>
#include <iomanip>
int main()
{
while(!std::cin.eof()) {
char ch = std::cin.get();
std::cout << char(ch == 0x30 ? 0x31 : ch);
}
return 0;
}
запуск:
example.exe <input.bin >output.bin
И да, я не спорю что в windows отвратительная поддержка posix, но с другой стороны, в С++ я уже не вижу таких проблем как в стандартной библиотеке С.
Если вы хотите иметь максимально похожее поведение на разных платформах (байт в байт), то будете вынуждены писать свои обертки над примитивами OS.
Эта магическая формула устанавливает стандартные потоки ввода и вывода в библиотеке времени выполнения C в двоичный режим.
Ничего магического, и можно прямо в Unicode переключить как ввод так и вывод:
_setmode( _fileno( stdin ), _O_U16TEXT );
_setmode( _fileno( stdout ), _O_U16TEXT );
С какими проблемами вы сталкивались, программируя для Windows на C и C++?
С проблемой, что нельзя удалить/переименовать файл, и сразу создать новый файл с таким же именем. А вот ввод-вывод с консоли - вообще по-барабану.
"библиотекам времени выполнения" - это же "runtime library"! Не сразу дошло. Но википедия говорит, что так можно.
Предположим у нас есть Posix-совместимый код на C или C++ (такого кода очень много, сомневаться не стоит). И у нас появляется задача - сделать код полностью кроссплатформеным. Большая часть дела и так сделана, данный код пашет абсолютно где угодно - Linux, FreeBSD, Mac OS, Android и IOS ибо Posix-совместимо (по большей части). Но Windows по правилам не играет - Windows хочет быть долбанутым пациентом дурки. И из-за этого у нас есть два стула:
оборачивать весь код макросами для совместимости и писать под Windows используя наиболее близкое к Posix окружение(Cygwin/MinGW и т.п.);
либо оборачивать код теми же макросами, но во вдое большем колличестве, чтобы на выходе получался совершенно другой код для MSVC.
Казалось бы "первый стул удобней", но то тут то там возникают весьма интересные проблемы из разряда отсутствия поддержки именно UTF-8 в функциях работы с файлами начиная с обычных Сишного fopen, закначивая всем std::filesystem. Posix окружения нет, а его очень сильно не хватает поэтому и существет целый набор эти костылей к Windows которые раз за разом шлефуются. По итогу, если ты используешь что-то что плохо работает в MinGW или Cygwin то добро пожаловать в MSVC - пиши в два раза больше кода.
Мне лично, да и большенству больше чем уверен плевать, что там за тёрки у Microsoft с их CRL и прочую их закрытую муть, если эта муть работает нормально. Программистам нужна переносимость на уровне исходного кода которая была в C и C++ изначально и без лишнего пердолинга с макросами для совместимости. Врятли кому-то хочется тратить лишние человекочасы на перенос кода на одну "не такую как все" платформу.
До С++20, т.к. нет поддержки std::u8string, с помощью специального адаптера нужно явно указать что строка в кодировке UTF-8:
const std::string uft8in = "...."; // UTF-8 строка
const std::path some_path = std::u8path(uft8in); // явно указываем что принимаем UTF-8
const std::string uft8out = some_path.u8string(); // явно указываем что хотим UTF-8
Думаю то, что у вас сейчас работает на Linux, это просто следствие того, что там везде подразумевается UTF-8, но это не строгое соответствие стандарту С++.
Суровая действительность разработки на C и C++ для Windows такова: для этой платформы никогда не существовало качественной, нативной реализации стандартной библиотеки этих языков.
Заявляет автор. А претензии идут к кодировке текста и кодовым страницам. Однако позвольте.. У Кернигана и Ричи никакой кодировки не было в принципе. Точнее, была одна на все - и на исходники, и на вывод сообщений. И, соответвенно, в stdlib никакой кодировки и кодовой страницы. И в, дай бог памяти, MSC 6.0, первом компиляторе для Windows, на уровне именно стандарных библиотек ничего этого не было. Вся поддержка окошек и пользовательского интерфейса шла через нестандарные для C/С++ библиотеки.
Говоря о командной строке Windows нельзя не заметить кросивое
Microsoft Windows [Version 10.0.22000.318]
(c) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.C:>chcp
Текущая кодовая страница: 866C:>example π αβγ
т.е. в W11 текущая кодовая страница досовская, но невосьмибиные символы через копипасту вставляются. и отображаются
т.е. в W11 текущая кодовая страница досовская, но невосьмибиные символы через копипасту вставляются. и отображаются
А что, не должны?)
Microsoft Windows [Version 6.1.7601]
(c) Корпорация Майкрософт (Microsoft Corp.), 2009. Все права защищены.C:>chcp
Текущая кодовая страница: 866C:>example ? ???
W7
При этом
int _tmain(int argc, _TCHAR* argv[])
{ _setmode(_fileno(stdout), _O_U8TEXT);
wprintf(L"example π αβγ\n");
вполне себе в консоль выводит
Это вы просто привели поведение в W7. Но это не ответ на вопрос "как консоль должна работать"!
Наверное, это связано с тем, что wprintf
в конечном итоге вызывает WriteConsoleW
? Принцип всё есть файл в Windows не работает.
Крайне сомнительные рассуждения о том, какие все правильные и хорошие, а у Microsoft руки кривые. C library была придумана задолго до Unicode и в какой-то момент автоматически перевести весь этот API на utf-8 означало бы просто сломать огромное количество уже написанного кода. Если приложению нужна unicode строка, оно должно в явном виде использовать wchar_t/wstring + те API, которые принимают данные этих типов.
Крайне сомнительные рассуждения о том, какие все правильные и хорошие, а у Microsoft руки кривые. C library была придумана задолго до Unicode и в какой-то момент автоматически перевести весь этот API на utf-8 означало бы просто сломать огромное количество уже написанного кода.Всё верно. Основная причина была именно в этом. Казалось бы кроссплатформенный POSIX API оказался не очень гибким в этом плане.
Если приложению нужна unicode строка, оно должно в явном виде использовать wchar_t/wstring + те API, которые принимают данные этих типов.И все же, в С++, я бы рекомендовал использовать UTF-8 как самую экономную и обратносовместимую кодировку и std::string/std::u8string соответственно. Также не забывайте, что в стандартной библиотеке, в некоторых случаях, у вас нет другой альтернативы, например в методе std::exception::what(). wchar_t/wstring — наиболее неудачная альтенатива, на мой взгляд, так как размер wchar_t по стандарту не детерминирован. На некоторых платформах он 2 байта, а на некоторых 4. А начиная с С++20 прямо стандартом рекомендуется использовать std::u8string, std::u16string, std::u32string для Unicode. В STL, в куче интерфейсов, перегрузки уже рассчитаны на это.
Размер wchar_t не детерменирован, но я не понимаю, как это мешаем мне его использовать.
Как передать std::wstring строку в std::exception, если это понадобится?
А в какой кодировке у вас будет строка std::wstring, в UTF16 или UFT32?
Вообще не имеет значения до той поры, пока вокруг меня API, которые работают с этим типом.
Как передать std::wstring строку в std::exception, если это понадобится?
Никак, это legacy API, который из-за своего возраста просто не совместим с unicode.
Вообще не имеет значения до той поры, пока вокруг меня API, которые работают с этим типом.Рано или поздно вы выйдете на API, которые предоставлены внешней системой, а не вашей C++ RTL, например
std::wstring fileName = ...;
HANDLE hFile = CreateFileW(fileName.c_str(), ...);
я так пишу, хотя понимаю что это неправильно. Не хочу загрязнять код учётом всех возможных размеров wchar_t.
Именно так у меня код и написан в платформозависимых частях большого кросс платформенного приложения.
CreateFileW ты можешь вызвать только на Windows, а на Windows в компиляторах MS у тебя wstring как раз правильного типа. Пишешь и даже задумываешься о том, какого у тебя там размера wchar_t.
У вас код — платформозависимый. Он всегда будет компилироваться под Windows, а там wchar_t всегда будет 2 байта.
Вообще не имеет значения до той поры, пока вокруг меня API, которые работают с этим типом.Никто вам не гарантирует что API вокруг будут работать попеременно то с UTF-16, то с UTF-32 в зависимости от размера вашего wchar_t. Вот с UTF-8 и std::string у вас не возникнет такой проблемы. На всех платформах (где char — 8 бит) мы просто создаем std::string и всё.
Никак, это legacy API, который из-за своего возраста просто не совместим с unicode.Тем не менее, на практике, UTF-8 там работает отлично с конвертацией из std::string и обратно.
Если приложению нужна unicode строка, оно должно в явном виде использовать wchar_t/wstringПроблема именно в этом «если». Приложению нужны разные строки, и от ascii-строк мы никуда не денемся. Заранее нельзя сказать, какая строка будет нужна будет для некоторого API, которое мы проектируем. Когда-то возникнет необходимость смешивать разные типы строк (например, в логах, или выводить на печать названия из конфигов, где конфиги традиционно ascii, а все интерфейсы печати unicode), и тогда привет — конвертация.
Кодировка UTF-8 изобретена в 1992 году, она была стандартизирована в январе 1993 года.Разработка Windows NT началась в 1989, когда UTF-8 ещё не существовало, и UCS-2 казалась будущим. Очевидно, что ближе к релизу никто не стал бы переделывать все API на использование UTF-8.
В июле 1993 года Microsoft, с выходом Windows NT 3.1, представила «широкий» API Windows, сделав ставку на кодировку UCS-2 (позже — UTF-16), а не на UTF-8. Это, как оказалось, было ошибкой, так как UTF-16 практически во всём уступает UTF-8. Правда, надо признать, что тогда некоторые проблемы не были особенно очевидными.
Капля здравого смысла для Windows-разработки на C и C++