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

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

А можно ссылочку, где A-API признается устаревшим самими авторами? Залез в MSDN - не вижу ничего про deprecated в CreateFileA, например...

Вы не видите потому что это не правда

А я уж подумал все, придется мне теперь только с W-версиями работать ;-)

Придётся работать еще и с CreateFile2

О, такого зверя я еще не встречал :-)

Просто если хочешь многоязыковую поддержку - забудь про них и все. Они не устарели, но большинство 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-битные слова — она очень соблазнительная и дорогого стоит.

Дело не только в эмодзи. Там еще куча всего: индексы, префиксы, умлаут, кратка и т.д. Даже в UTF-32, где код-поинт равен код-юниту, символ всё равно может состоять из нескольких код-поинтов и запросто занимать больше 4х байт.

Значит я что-то путаю)

Суррогатные пары...

Об этом на сайте самих 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 и лигатуры. Последнее вообще делает количество символов в конкретной строке шрифтозависимым.

При этом в огромном числе задач нам вообще плевать на юникод. Имя файла — это последовательность байт, а сколько там символов или какого цвета какашка-эмодзи, нас вообще не волнует.

На этом могут работать различные уязвимости, когда одним способом длина строки посчиталась так, а другим способом — иначе. И вот у нас уже buffer overflow.

В идеале да, неплохо бы сделать 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 секунды

А что тут удивительного. Любой STL контейнер проиграет почти любому специально заточенному контейнеру под те или иные данные и условия. На реализацию STL накладываются следующие условия:
  • интерфейс
  • универсальность по данным
  • гарантии по итераторам
  • гарантии по исключениям

Э-э-э, а где вы в C# нашли std::map?

Bы серьёзно? std::basic_string<T> ... std::basic_string_view<T>. А UCS-2 в отличии от UTF-8 было просто удобнее читать и считать символы/байты (wchar_t / 2) - не надо анализировать токены.

Это раньше UCS-2, а теперь там UTF-16. И те же сложности, что и с UTF-8.

А можно поподробнее объяснить какую проблему мы решаем в С++? Вот прямо сейчас посмотрел как работает С++ Runtime от Microsoft внутри: там std::cout после себя передает консоли просто байты, не интерпретируя их каким-то особым образом.
Вот уже лет 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, от этого сильно зависим.
Хотел бы обратить ваше внимание на то, что я спрашивал только в контексте С++, не С и все замечания были относительно самого низкоуровневого ввода/вывода в консоль windows и поддержки UTF-8 через потоки ввода/вывода. Сам вывод ничего не делает с кодировкой и никак не меняет байтовый поток. Я так уверенно говорю, потому-что сам прокидывал случайные бинарные данные через стандартный ввод/вывод и никаких проблем не было, тем более с UTF-8.
Не знаю как работает рантайм C библиотеки, а могу лишь догадываться, что \r\n на \n меняет именно он, раз на низком уровне мы всегда работаем с потоком байт. Хотелось бы конечно увидеть весть пример с комментариями, что вы хотите получить и что выходит на самом деле. На мой взгляд, если уж под windows принято заменять \n на \r\n, то все функции чтения/записи строк тогда уж должны это делать единообразно. Опять же это никак не решает задачу передачи таких файлов с одной систему на другую. На мой взгляд проблема гораздо глубже чем ввод/вывод в консоль.
Ну вот например программа на C++. Которая по идее в бинарном файле должна заменять 0x30 на 0x31, не трогая другие байты. Так она под windows 0x0A заменяет на пару 0x0D, 0x0A
#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
Сделал, поведение не изменилось. MSVS 2019
Тогда только _setmode
_setmode( _fileno( stdin ), _O_BINARY );
_setmode( _fileno( stdout ), _O_BINARY );
Честно, я потерял нить того что мы обсуждаем. Вас смущает что один и тот же код, строчка в строчку, дает разные сайд эффекты на разных платформах ?! Ну так это всегда было, есть и будет. Писать кроссплатформенные программы не так просто, нужно учитывать все эти нюансы: разную архитектуру, разную OC, разную файловую систему, разделители, порядок байт, размер целых и т.д.
И да, я не спорю что в windows отвратительная поддержка posix, но с другой стороны, в С++ я уже не вижу таких проблем как в стандартной библиотеке С.
Если вы хотите иметь максимально похожее поведение на разных платформах (байт в байт), то будете вынуждены писать свои обертки над примитивами OS.
Обсуждаем это
Вопрос только в контексте о том, зачем нужна сторонняя библиотека.
Вроде как с библиотекой MSVC начинает работать, как в posix-системах, без обёрток.
Вроде как с библиотекой MSVC начинает работать, как в posix-системах, без обёрток.
Это не так, если внимательно ознакомиться со статьёй.

Эта магическая формула устанавливает стандартные потоки ввода и вывода в библиотеке времени выполнения C в двоичный режим.

Ничего магического, и можно прямо в Unicode переключить как ввод так и вывод:

_setmode( _fileno( stdin ), _O_U16TEXT );
_setmode( _fileno( stdout ), _O_U16TEXT );

Надо было считать данные с edit в программу написанной на winapi, а там значения не char*, а wchar* потом их перевести в char*, и вывести в cout, а wchar* выводится в консоль через wcout

С какими проблемами вы сталкивались, программируя для Windows на C и C++?

С проблемой, что нельзя удалить/переименовать файл, и сразу создать новый файл с таким же именем. А вот ввод-вывод с консоли - вообще по-барабану.

"библиотекам времени выполнения" - это же  "runtime library"! Не сразу дошло. Но википедия говорит, что так можно.

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

Насчет fopen согласен, но это все С. Насчет С++ и std::filesystem — вы что-то не так делает, потому-что внутри он полностью юникодный и скорее всего у вас проблема где-то на входе или выходе. Идеологически правильно, в С++20 и дальше, передавать std::filesystem строки std::u8string, std::u16string, std::u32string. Это не просто строки с разным размером типа CharT. Стандартом подразумевается что это именно UTF-8/16/32 строки.
До С++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
Текущая кодовая страница: 866

C:>example π αβγ

т.е. в W11 текущая кодовая страница досовская, но невосьмибиные символы через копипасту вставляются. и отображаются

т.е. в W11 текущая кодовая страница досовская, но невосьмибиные символы через копипасту вставляются. и отображаются

А что, не должны?)

Microsoft Windows [Version 6.1.7601]
(c) Корпорация Майкрософт (Microsoft Corp.), 2009. Все права защищены.

C:>chcp
Текущая кодовая страница: 866

C:>example ? ???

W7

При этом

int _tmain(int argc, _TCHAR* argv[])
{ _setmode(_fileno(stdout), _O_U8TEXT);	
	wprintf(L"example π αβγ\n");

вполне себе в консоль выводит

Это вы просто привели поведение в W7. Но это не ответ на вопрос "как консоль должна работать"!

какой смысл в этом вопросе? Я не разработчик консолей, вы скорей всего тоже. Вот у нас есть данность - в W7 консоль работает так, в W10-11 - немного иначе, а пользоваться в це всей этой радостью с бубном и плясками - вот так, или вот эдак.

Наверное, это связано с тем, что wprintf в конечном итоге вызывает WriteConsoleW ? Принцип всё есть файл в Windows не работает.

Нет. Вы просто путаете разные API. Есть отдельный API консоли, где вы указываете handle именно консоли. Их может быть несколько у приложения. Там можно выводить текс в разных позициях, в разных режимах, разным цветом и т.д… Также есть API стандартного ввода-вывода, которому все равно что передается, он передает байты и там всё есть файл: ReadFile/WriteFile.

Крайне сомнительные рассуждения о том, какие все правильные и хорошие, а у 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, в UTF-16 или UTF-32? Просто при загрузке/выгрузке данных в/из std::wstring вы будете вынуждены учитывать сразу оба варианта кодировки и это очень не удобно на практике.
Как передать 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 байта.
Ну хорошо, другой пример. Мне нужно записать в файл строку UTF-16, например, для mp3-тега ID3v2. Без задней мысли беру и пишу строку wstr.c_str(), а на линуксах потом не заработает.
Не заработает. Если мы данные в UTF кодировке записываем в файл или передаем по сети, то ко всему, дополнительно, появляется проблема с порядком байтов: big-endian/little-endian. Т.е. по сути возможных кодировок для std::wstring становится 4-ре: UTF16-LE, UTF16-BE, UTF32-LE, UTF32-BE. Это еще одна причина использовать UTF-8 везде где можно — в ней нет проблем с порядком байт.
Вообще не имеет значения до той поры, пока вокруг меня 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 года.
В июле 1993 года Microsoft, с выходом Windows NT 3.1, представила «широкий» API Windows, сделав ставку на кодировку UCS-2 (позже — UTF-16), а не на UTF-8. Это, как оказалось, было ошибкой, так как UTF-16 практически во всём уступает UTF-8. Правда, надо признать, что тогда некоторые проблемы не были особенно очевидными.
Разработка Windows NT началась в 1989, когда UTF-8 ещё не существовало, и UCS-2 казалась будущим. Очевидно, что ближе к релизу никто не стал бы переделывать все API на использование UTF-8.
Atmel Studio 7, основанная на VS2015, не имеет опции «кодировка файла по-умолчанию». Из-за этого она то сохраняет исходники в 1251, то в UTF-8, ломая русские строчки. Единственный способ, который нам известен, сохранять файлы в UTF-8 с BOM, а также добавлять один русский комментарий в каждый файл. Но это нужно делать руками при создании новых файлов.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий