C# — Кодировки java в с#

Содержание

C# Работа с файлами и кодировки

Иногда одна строчка кода стоит огромного количества сил и времени. Но зачем терять время на изобретение велосипедов. Ведь кто-то это уже точно писал до нас. Поэтому было принято волевое решение делать несколько постов в неделю, в которых я буду писать небольшие блоки кода, которые зачастую просто вылетают из головы в самый неподходящий момент. В одном выпуске будет 3-5 небольших блока кода с подробным описанием и особенностями использования.

1. Получение списка файлов в папке

Есть много вариантов. Но мы рассмотрим два:

Directory.GetFiles(string path) возвращает массив строк, где каждая строка — это путь к файлу, который был найден в данном каталоге. Но данный код будет вести поиск только в данной папке, то есть файлы в подпапках учитываться не будут. Данный метод имеет возможность фильтрации. Например, если нам нужно отобразить только все картинки формата JPG — можно использовать следующий код:

Это уже позволяет осуществлять фильтрацию, однако это всё ещё поиск без подпапок. Внимательно посмотрев все перегрузки данного метода можно обнаружить, что его всё-таки можно заставить искать все содержимое данной папки. Для этого нужно просто добавить ещё один параметр:

SearchOption.AllDirectories. Если посмотреть какие вариант есть ещё, то окажется, что их всего два:

Как видно из названия — второй пункт используется по умолчанию.

Следующий код ищет все файлы в папке, включая те, что расположены в подпапках:

2. Русская кодировка в файлах

Думаю все сталкивались с ситуацией, когда при чтении/записи русского текста выводились/сохранялись страшные символы не имеющие ничего общего с тем текстом, с которым вы работали.

Многие воспринимают это как «кривость», или «баг». Нет, ребята. Это не баг — это фича =).

Как все мы знаем — текст сохраняется в определенной кодировке. Где каждому возможному символу соответствует определенный байт (или даже 2 байта). Поэтому есть однобайтовые кодировки и двубайтные кодировки.

Преимущества и недостатки данных кодировок очевидны — однобайтная кодировка содержит в себе только 256 символов (один байт = 8 бит = 2^8 (так как бит равен или 0, или 1) = 256). Если предположить, что в алфавите 30 букв, то 3 алфавита — это 90 символов. Их нужно умножить на два, так как буквы могут быть маленькими и большими. Выходит 180 символов. Остается всего 76 символов. Выходит минус однобайтовой кодировки в том, что она сильно ограничена символами. А плюс, соответственно, в том, что она маловесна. И текст из 1000 символов будет иметь размер лишь 1000 байт. В то время у двубайтной кодировки вес будет уже 2000 байт. Зато у двубайтной кодировки уже возможно 65536 символов.

Именно из-за того, что кодировок много — каждому тяжело угодить даже с двубайтной кодировкой. Поэтому если вы хотите, чтобы российский текст корректно сохранялся и читался — нужно указывать необходимую кодировку. Обычно используется кодировка Windows-1251.

лучшее кодирование для двоичного файла в java и С#

Я знаю, что существует много типов кодирования и декодирования, и из того, что я прочитал, base64 — отличный выбор, когда дело доходит до кодирования двоичного файла (изображения, mp3, видео).

Теперь, когда дело доходит до декодирования, мне нужно будет преобразовать из base64, а затем получить строковое значение. процесс получения строки после декодирования, мне потребуется сделать это (в С#): System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);

здесь я заметил, что у меня есть несколько вариантов того, что нужно использовать для получения строки, например ASCII, UNICODE, DEFAULT.

реальный вопрос в этом сообщении — это если im использовать java для кодирования и С# для декодирования двоичного файла, каково наилучшее решение/выбор, я должен использовать? Я пробовал несколько методов, и некоторые из символов не могли быть прочитаны, таким образом выдают знак вопросительного знака (?).

однако наиболее близкое кодирование, которое может быть прочитано байтом, — это когда im использует это в Java: String encoded = Base64.encodeToString(fileData, Base64.CRLF); между тем в С# im, используя это: byte[] encodedDataAsBytes = System.Convert.FromBase64String(encodedData); string returnValue = System.Text.Encoding.ASCII.GetString(encodedDataAsBytes); byte[] encodedDataAsBytes = System.Convert.FromBase64String(encodedData); string returnValue = System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);

Тем не менее, есть несколько символов, которые невозможно прочитать. У кого-нибудь есть решение для этой проблемы? любая обратная связь очень ценится. спасибо за продвижение.

java c# binaryfiles decode

4 ответа

3 Решение Marc Gravell [2012-10-12 10:28:00]

Вы заявляете, что этот вход — «образ, mp3, видео», поэтому: произвольный двоичный файл. Затем вы указываете, что используете base-64, а именно: по какой-то причине вам необходимо перенести/сохранить эти данные в виде строки (обратите внимание: передача/хранение в качестве исходного двоичного файла обычно предпочтительнее — у базового 64 есть накладные расходы).

Теперь, когда дело доходит до декодирования, мне нужно будет преобразовать из base64, а затем получить строковое значение.

Там проблема; здесь нет строкового значения. «Изображение, mp3, видео» просто не является «строковым значением». То, что вы можете сделать, это декодировать с базы-64 обратно на исходный двоичный файл (тривиально в java или С#), но это все, что вы можете сделать. Если вам понадобилось «строковое значение» из необработанного двоичного файла, единственное, что вы можете сделать, это перекодировать его с помощью base-64 (который вернет вам то, с чего вы начали) или какой-нибудь другой base-n.

Текстовое кодирование, такое как ASCII или UTF-8, имеет смысл только в том случае, если бинарные данные фактически являются текстовыми данными, хранящимися в этой кодировке. Вы не можете использовать UTF-8 для «декодирования» двоичного кода, который фактически не является UTF-8.

Дело в бинарных файлах состоит в том, что они двоичные (тип byte[] ). Большую часть времени вы не можете преобразовать байты непосредственно в строку (используя Encoding.GetString(byte[]) ), потому что некоторые из них могут иметь значения, которые не могут быть представлены в строке (это то, что вы испытываете).

Преобразование двоичных данных в строку с использованием Encoding.GetString(byte[]) для ее преобразования в BASE64 не имеет смысла, поскольку вы теряете информацию при преобразовании двоичной информации в строку — вам нужно будет преобразовать ее непосредственно в BASE64.

Преобразование строкового представления BASE64 байтового массива в byte[] в порядке — это возвращает исходные двоичные данные. Однако преобразование этого byte[] в string не подходит по причине, указанной выше.

Как предполагается, что кодирование BASE64 работает:

  1. Получить двоичные данные в виде byte[]
  2. Создать строку BASE64 из byte[]
  3. Передача строки BASE64
  4. Создать byte[] из строки BASE64
  5. Продолжить работу с byte[]

1 Terry Cl. [2012-10-12 10:28:00]

Если вы хотите получить строку после декодирования ваших данных, это означает, что ваши данные каким-то образом в текстовом формате. Если это так, вы должны знать начальную кодировку файла, такую как UTF-8. Затем вы можете правильно декодировать строки. Если ваша программа только передает файлы из одного места в другое без каких-либо действий с ее содержимым, лучше оставить их при декодировании.

1 hyde [2012-10-12 10:34:00]

  1. Преобразуйте строковый объект (Java или С#) в байтовый массив, используя кодировку UTF-8 (или некоторую другую, если у вас есть причина).
  2. У вас теперь есть двоичные данные, кодированный текст UTF-8 должен быть конкретным. Если вам нужно перенести его где-нибудь, что не поддерживает исходные двоичные данные или текст UTF-8, или если вы не хотите беспокоиться о некоторых символах, имеющих особое значение, например, в XML, преобразуйте его в строку ASCII с использованием кодировки base64.
  3. Выполняйте все, что пожелаете, с помощью строки ASCII (base64 даже разрешает некоторые пробелы и т.д.), Чтобы получить его в декодере.
  4. Преобразование строки ASCII обратно в массив байтов с декодированием base64.
  5. Преобразование байтового массива обратно в строковый объект (С# или Java) с использованием кодировки UTF-8.

Если двоичные данные или текст UTF-8 в порядке, вы можете пропустить шаги 2 и 4. Но нужны 1 и 5, потому что в таких языках, как С# и Java, строка является «логическими символами», это не байты, которые вы можете сохранить или перенести (конечно, это байты в памяти, как правило, UTF-16 или UTF-32, но вы не должны этого волновать). Он должен быть преобразован в байты с использованием некоторой кодировки. UTF-x являются единственными, которые не теряют никаких символов, а UTF-8 является наиболее экономичным по площади, если большинство символов являются «западными» алфавитами.

Одна особенность base64 заключается в том, что хотя это фактически 7-разрядные символы ASCII, вы можете поместить текст с кодировкой base64 в строковый объект С#/Java и обратно в кодированный байтовый массив base64 с использованием любой строковой кодировки, поскольку все используемые кодировки строк являются надмножествами 7-битного ASCII. Таким образом, вы можете взять данные изображения, base64 закодировать его и поместить результирующий текст в объект String без забот об кодировании и повреждении.

Шаги для двоичных файлов:

  1. Получите содержимое двоичного файла, такого как файл изображения PNG, в массив байтов.
  2. То же, что и на шаге 2 выше, кроме данных не UTF-8.
  3. То же, что и на шаге 3 выше
  4. То же, что и на шаге 4 выше
  5. Теперь у вас есть массив байтов, содержащий содержимое файла PNG с шага 1.

Помогите перевести код из C# в JAVA

Учеба и Работа /

Сделайте за меня

25 июн 2010 10:20
25 июн 2010 10:21
25 июн 2010 10:22
25 июн 2010 10:37

Ну они же похожи. Языки-то. И не очень сложные оба. После java вас C# не сильно напугает. Вы главное не отчаивайтесь.

Вы переводите, а когда _конкретные_ проблемы будут — сообщайте. Тем более большую часть вы уже, видимо, сделали. %)

Просто не уверен что все так от скуки бросятся чужой работой заниматься. Хотя возможно тут альтруисты есть. А больших денег такая маленькая задача, вероятно, не стоит. ;-)))

25 июн 2010 10:43

Конкретно как написать на JAVA?:

25 июн 2010 10:52
25 июн 2010 11:34

Мне C# не нравится (похож на вычурную, поддельную java) — но периодически оказываюсь в похожей ситуации — в основном модифицировать чужие проекты приходится. Не скажу чтобы я в восторге был от таких задач, но делать нечего. Поэтому боюсь что второпях я вам большой помощи не смогу оказать. ;-)))

foreach вы можете перевести прямо так, там есть конструкция. м-м-м. не затупить бы самому:

for(GenerateHTMLTest x : cells)

может это поможет? Сами условия и т.п. вы, вроде, правильно записали. В вашем коде переведённом я если честно не заметил связи между x и cells.

25 июн 2010 11:47
25 июн 2010 13:16
25 июн 2010 13:33

Хм. сейчас не могу проверить/потестить, так что только посоветовать остаётся:

Нужно создать файл и записывать в него строчки, я так понимаю.

Покатит, кажется, класс

FileWriter
либо даже
PrintWriter

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

25 июн 2010 13:55
25 июн 2010 14:11

Ну это как я подозреваю по тексту (проверить сейчас не могу) класс для форматированного вывода в текстовый файл. WriteLine — метод который записывает строку и переходит на новую.

В java близкими аналогами, я думаю, будут именно PrintWriter и его метод println. Как их использовать я вкратце показал. Ну с гугла примеры ещё найдёте или в доке по API гляньте. Но вроде можно прямо так заменить.

25 июн 2010 14:52
25 июн 2010 15:59

А это будет посложнее. ;-)

Не то чтобы сложно, но не так тривиально как с Outputoм. Тут во-первых класс для чтения нужно какой-то другой взять, во-вторых для разбора строки что-то.

Хотя я думаю что лучше построить Scanner на основе FileInputStream и с его помощью читать числа прямо так. Там есть метод nextInt вместо используемого здесь разбиения строки и parseIntов.

Мне сейчас надо территориально перемещаться. Когда дома буду — загляну в форум. Если получится — продвиньтесь за это время немножко. %)

(Короче, парсить файлы это вам не пианино тырить. %)

C# против Java: какой язык программирования общего назначения выбрать?

Что такое C#?

Что такое JAVA?

C# VS. JAVA: ОСНОВНЫЕ СХОДСТВА

  • Безопасность типов. Ошибка типа возникает, когда тип данных одного объекта ошибочно назначается другому объекту, создавая непреднамеренные побочные эффекты. И C#, и Java работают на то, чтобы гарантировать выявление таких типов незаконных приведений во время компиляции. Если приведение не может быть применено к новому типу, тогда во время выполнения такие исключения будут удалены.
  • Сборка мусора: На языках более низкого уровня управление памятью может быть утомительным, ведь нужно помнить о том, что необходимо правильно удалить новые объекты, чтобы освободить ресурсы. На С# и Java есть встроенная сборка мусора, которая помогает предотвратить утечку памяти путем удаления объектов, которые больше не используются приложением. Утечки памяти все еще могут возникать, но благодаря основам управления памятью — это уже не ваша проблема.
  • Одиночное наследование. Оба языка поддерживают одиночное наследование – это означает, что существует только один путь из любого базового класса в любой из его производных классов. Это ограничивает непреднамеренные побочные эффекты, которые могут возникать при наличии нескольких путей между несколькими базовыми классами и производными классами. Diamond pattern – книжный пример этой проблемы.
  • Интерфейсы. Интерфейс представляет собой абстрактный класс, где все методы абстрактны. Абстрактным методом является тот метод, который объявлен, но не содержит подробностей его реализации. Код, определяющий любые методы или свойства, определенные интерфейсом, должен предоставляться классом, который его реализует. Это помогает избежать двусмысленности паттерна diamond, поскольку всегда ясно, какой базовый класс реализует данный производный класс во время выполнения. Результатом является чистая иерархия линейных классов одиночного наследования в сочетании с некоторой универсальностью множественного наследования. Фактически использование абстрактных классов является одним из способов множественного наследования языков, которые могут преодолеть проблему паттерна diamond.

C# VS. JAVA: ОСНОВНЫЕ РАЗЛИЧИЯ

Важно помнить, что C# берет свое начало в желании Microsoft иметь собственный «Java-подобный» язык для платформы .NET. Поскольку C# не создавался в вакууме, новые функции были добавлены и настроены для решения проблем, с которыми сталкивались разработчики Microsoft, когда они изначально пытались создать свою платформу на Visual J++. В то же время сообщество Java с открытым исходным кодом продолжало расти и между этими двумя языками развивалась гонка технических вооружений. Вот некоторые из основных различий между C# и Java.

  • Windows vs open-source. Хотя существуют реализации с открытым исходным кодом, C# в основном используется в разработке для платформ Microsoft – .NET Framework CLR и является наиболее широко используемой реализацией CLI. На другом конце спектра Java имеет огромную экосистему с открытым исходным кодом и у него открылось второе дыхание отчасти благодаря тому, что Google использует JVM для Android.
  • Поддержка обобщений (Generics): Generics улучшает проверку типов с помощью компилятора, в основном удаляя приведения из исходного кода. В Java средства обобщений реализуются с использованием стираний. Параметры общего типа «стираются», а при компиляции в байт-код добавляются приведения. C# также использует обобщения, интегрируя его в CLI и предоставляя информацию о типе во время выполнения, что дает небольшое увеличение производительности.
  • Поддержка делегатов (указателей): В C# есть делегаты, которые по существу служат в качестве методов, которые могут быть вызваны без знания целевого объекта. Для достижения такой же функциональности в Java вам необходимо использовать интерфейс с одним методом или другим способом обхода, который может потребовать нетривиального количества дополнительного кода, в зависимости от приложения.
  • Проверяемые исключения: Java различает два типа исключений – проверяемые и непроверяемые. C# выбрал более минималистский подход, имея только один тип исключения. Хотя способность ловить исключения может быть полезна, она также может отрицательно влиять на масштабируемость и контроль версий.
  • Полиморфизм: C# и Java используют очень разные подходы к полиморфизму. Java допускает полиморфизм по умолчанию, C# же должен вызывать ключевое слово «virtual» в базовом классе и ключевое слово «override» в производном классе.
  • Перечисления (Enums): в C# перечисления представляют собой простые списки именованных констант, где базовый тип должен быть целым. Java представляет перечисления более глубоко, рассматривая его как именованный экземпляр типа, что упрощает добавление пользовательского поведения к отдельным перечислениям.

Когда стоит использовать C# или Java?

Язык, который вы в конечном итоге решите использовать, будет во многом зависеть от платформы, которую вы выбрали для своего проекта. Сегодня C# используется в основном для реализации CLI на .NET Framework, Mono и Portable.NET. Если ваше программное обеспечение или веб-приложение создаются для Windows, C# будет работать лучше всего с набором технологий .NET.

Тем не менее, если вы хотите разрабатывать для Unix, Linux или других платформ за пределами платформы Microsoft, экосистема с открытым исходным кодом – Java – лучший выбор. Сообщество постоянно создает новые библиотеки и инструменты. Появились новые мощные языки, такие как Scala, Clojure и Groovy, и они все тоже основаны на JVM. К тому же это неплохо, что большинство реализаций JVM являются общедоступными и бесплатными. Java – основной язык разработки, который использует Google для Android – самой большой мобильной операционной системы в мире в настоящее время.

Имейте в виду, что перечисленные выше преимущества незначительны, и ни один из языков не исчезнет в ближайшее время. Оба языка существуют достаточно долго и, на самом деле, вы не сможете ничего такого построить на одном языке, чего бы не смогли построить на другом.

Итог: выберите язык, который лучше всего подходит для платформы вашего проекта.

Кодировка символов в .NET Character Encoding in .NET

Символы — это абстрактные сущности, которые могут быть представлены различными способами. Characters are abstract entities that can be represented in many different ways. Кодировка — это система, где с каждым символом поддерживаемого набора символов сопоставляется значение, представляющее этот символ. A character encoding is a system that pairs each character in a supported character set with some value that represents that character. Например, азбука Морзе — это кодировка, в которой каждому символу латинского алфавита соответствует набор точек и тире, которые можно передавать с помощью телеграфа. For example, Morse code is a character encoding that pairs each character in the Roman alphabet with a pattern of dots and dashes that are suitable for transmission over telegraph lines. Компьютерная кодировка — это система, где с каждым символом поддерживаемого набора символов сопоставлено числовое значение, представляющее этот символ. A character encoding for computers pairs each character in a supported character set with a numeric value that represents that character. Кодировка состоит из двух компонентов: A character encoding has two distinct components:

кодировщик, преобразующий последовательность символов в последовательность числовых значений (байтов); An encoder, which translates a sequence of characters into a sequence of numeric values (bytes).

декодер, преобразующий последовательность байтов в последовательность символов. A decoder, which translates a sequence of bytes into a sequence of characters.

Кодировка описывает правила, по которым работают кодировщик и декодер. Character encoding describes the rules by which an encoder and a decoder operate. Например, класс UTF8Encoding описывает правила кодирования и декодирования для формата UTF-8, в котором используется от одного до четырех байтов для представления одного символа Юникода. For example, the UTF8Encoding class describes the rules for encoding to, and decoding from, 8-bit Unicode Transformation Format (UTF-8), which uses one to four bytes to represent a single Unicode character. В процессе кодирования и декодирования также может выполняться проверка. Encoding and decoding can also include validation. Например, класс UnicodeEncoding предназначен для проверки допустимости пар, составляемых всеми символами-заместителями. For example, the UnicodeEncoding class checks all surrogates to make sure they constitute valid surrogate pairs. (Пара символов-заместителей состоит из символа с кодовой точкой в диапазоне от U+D800 до U+DBFF и символа с кодовой точкой в диапазоне от U+DC00 до U+DFFF.) Резервная стратегия определяет, как кодировщик обрабатывает недопустимые символы или как декодер обрабатывает недопустимые байты. (A surrogate pair consists of a character with a code point that ranges from U+D800 to U+DBFF followed by a character with a code point that ranges from U+DC00 to U+DFFF.) A fallback strategy determines how an encoder handles invalid characters or how a decoder handles invalid bytes.

Классы кодировок .NET позволяют хранить и преобразовывать символьные данные. .NET encoding classes provide a way to store and convert character data. Их не следует использовать для хранения двоичных данных в строковом виде. They should not be used to store binary data in string form. В зависимости от используемой кодировки преобразование двоичных данных в строковый формат с использованием классов кодировок может привести к неожиданному результату и неточным или поврежденным данным. Depending on the encoding used, converting binary data to string format with the encoding classes can introduce unexpected behavior and produce inaccurate or corrupted data. Для преобразования двоичных данных в строковый формат используйте метод Convert.ToBase64String . To convert binary data to a string form, use the Convert.ToBase64String method.

.NET использует для представления символов и строк кодировку UTF-16 (представленную классом UnicodeEncoding). .NET uses the UTF-16 encoding (represented by the UnicodeEncoding class) to represent characters and strings. В приложениях, предназначенных для среды CLR, кодировщики используются для сопоставления представлений символов Юникода, поддерживаемых средой CLR, с другими схемами кодирования. Applications that target the common language runtime use encoders to map Unicode character representations supported by the common language runtime to other encoding schemes. Декодеры служат для сопоставления символов различных кодировок с Юникодом. They use decoders to map characters from non-Unicode encodings to Unicode.

В этом разделе: This topic consists of the following sections:

Кодировки в .NET Encodings in .NET

Все классы кодировок в .NET наследуют от абстрактного класса System.Text.Encoding, который определяет общую для всех кодировок функциональность. All character encoding classes in .NET inherit from the System.Text.Encoding class, which is an abstract class that defines the functionality common to all character encodings. Для доступа к отдельным объектам кодировок, реализованным в .NET, можно сделать следующее: To access the individual encoding objects implemented in .NET, do the following:

Используйте статические свойства класса Encoding, которые возвращают объекты, представляющие стандартные кодировки символов .NET (ASCII, UTF-7, UTF-8, UTF-16 и UTF-32). Use the static properties of the Encoding class, which return objects that represent the standard character encodings available in .NET (ASCII, UTF-7, UTF-8, UTF-16, and UTF-32). Например, свойство Encoding.Unicode возвращает объект UnicodeEncoding . For example, the Encoding.Unicode property returns a UnicodeEncoding object. Каждый объект использует резервную стратегию замены для обработки строк, которые он не может закодировать, и байтов, которые не может декодировать. Each object uses replacement fallback to handle strings that it cannot encode and bytes that it cannot decode. (Дополнительные сведения см. в разделе Replacement Fallback .) (For more information, see the Replacement Fallback section.)

Вызвать конструктор класса кодировки. Call the encoding’s class constructor. Таким образом могут быть созданы объекты для кодировок ASCII, UTF-7, UTF-8, UTF-16 и UTF-32. Objects for the ASCII, UTF-7, UTF-8, UTF-16, and UTF-32 encodings can be instantiated in this way. По умолчанию каждый объект использует резервную стратегию замены для обработки строк, которые он не может закодировать, и байтов, которые он не может декодировать, но вы можете указать, чтобы вместо этого создавалось исключение. By default, each object uses replacement fallback to handle strings that it cannot encode and bytes that it cannot decode, but you can specify that an exception should be thrown instead. (Дополнительные сведения см. в разделах Replacement Fallback и Exception Fallback .) (For more information, see the Replacement Fallback and Exception Fallback sections.)

Вызвать конструктор Encoding.Encoding(Int32) и передать ему целое число, представляющее кодировку. Call the Encoding.Encoding(Int32) constructor and pass it an integer that represents the encoding. Объекты стандартных кодировок используют резервные стратегии замены, а объекты кодовых страниц и двухбайтовых кодировок (DBCS) используют резервную стратегию наилучшего соответствия для обработки строк, которые не удается закодировать, или байтов, которые не удается декодировать. Standard encoding objects use replacement fallback, and code page and double-byte character set (DBCS) encoding objects use best-fit fallback to handle strings that they cannot encode and bytes that they cannot decode. (Дополнительные сведения см. в разделе Best-Fit Fallback .) (For more information, see the Best-Fit Fallback section.)

Вызовите метод Encoding.GetEncoding, возвращающий любую стандартную кодировку, кодовую страницу или кодировку DBCS, доступную в .NET. Call the Encoding.GetEncoding method, which returns any standard, code page, or DBCS encoding available in .NET. Перегрузки позволяют задать резервный объект как для кодировщика, так и для декодера. Overloads let you specify a fallback object for both the encoder and the decoder.

В стандарте Юникода каждому символу в каждом поддерживаемом символьном наборе присваивается кодовая точка (номер) и имя. The Unicode Standard assigns a code point (a number) and a name to each character in every supported script. Например, символ «A» представляется кодовой точкой U+0041 и именем LATIN CAPITAL LETTER A. For example, the character «A» is represented by the code point U+0041 and the name «LATIN CAPITAL LETTER A». Кодировки UTF определяют способы кодирования кодовой точки в виде последовательности из одного или нескольких байтов. The Unicode Transformation Format (UTF) encodings define ways to encode that code point into a sequence of one or more bytes. Схема кодировки Юникод упрощает разработку международных приложений, так как позволяет представлять символы любых наборов символов в единой кодировке. A Unicode encoding scheme simplifies world-ready application development because it allows characters from any character set to be represented in a single encoding. Разработчикам приложений больше не нужно сохранять данные о схеме кодировки, которая использовалась для представления символов конкретного языка или системы письма. Передача данных между системами, использующими различные языки, может происходить без искажений. Application developers no longer have to keep track of the encoding scheme that was used to produce characters for a specific language or writing system, and data can be shared among systems internationally without being corrupted.

Платформа .NET поддерживает три кодировки, определенные стандартом Юникод: UTF-8, UTF-16 и UTF-32. .NET supports three encodings defined by the Unicode standard: UTF-8, UTF-16, and UTF-32. Дополнительные сведения см. в описании стандарта Юникод на домашней странице Юникода. For more information, see The Unicode Standard at the Unicode home page.

Информацию обо всех доступных в .NET кодировках можно получить, вызвав метод Encoding.GetEncodings. You can retrieve information about all the encodings available in .NET by calling the Encoding.GetEncodings method. Платформа .NET поддерживает системы кодирования символов, перечисленные в таблице ниже. .NET supports the character encoding systems listed in the following table.

кодировка Encoding Класс Class ОПИСАНИЕ Description Преимущества и недостатки Advantages/disadvantages
ASCII ASCII ASCIIEncoding Кодирует ограниченный диапазон символов, используя семь младших битов байта. Encodes a limited range of characters by using the lower seven bits of a byte. Так как эта кодировка поддерживает только значения символов от U+0000 до U+007F, то в большинстве случаев она не отвечает требованиям международных приложений. Because this encoding only supports character values from U+0000 through U+007F, in most cases it is inadequate for internationalized applications.
UTF-7 UTF-7 UTF7Encoding Представляет символы в виде последовательностей 7-разрядных символов ASCII. Represents characters as sequences of 7-bit ASCII characters. Символы Юникода, не относящиеся к ASCII, представляются в виде escape-последовательности символов ASCII. Non-ASCII Unicode characters are represented by an escape sequence of ASCII characters. UTF-7 поддерживает протоколы, например протоколы электронной почты и группы новостей. UTF-7 supports protocols such as email and newsgroup protocols. Однако формат UTF-7 недостаточно безопасен и надежен. However, UTF-7 is not particularly secure or robust. В некоторых случаях изменение одного бита может привести к существенному изменению интерпретации всей строки UTF-7. In some cases, changing one bit can radically alter the interpretation of an entire UTF-7 string. В других случаях для кодировки одного и того же текста могут использоваться разные строки UTF-7. In other cases, different UTF-7 strings can encode the same text. В последовательностях, содержащих отличные от ASCII символы, формат UTF-7 требует больше места, чем UTF-8, а кодирование и декодирование выполняются медленнее. For sequences that include non-ASCII characters, UTF-7 requires more space than UTF-8, and encoding/decoding is slower. Поэтому по возможности лучше использовать UTF-8 вместо UTF-7. Consequently, you should use UTF-8 instead of UTF-7 if possible.
UTF-8 UTF-8 UTF8Encoding Представляет каждую кодовую точку Юникода в виде последовательности от одного до четырех байтов. Represents each Unicode code point as a sequence of one to four bytes. UTF-8 поддерживает 8-разрядный размер данных и хорошо работает со многими операционными системами. UTF-8 supports 8-bit data sizes and works well with many existing operating systems. Для диапазона символов ASCII кодировка UTF-8 идентична кодировке ASCII и предоставляет более широкий набор символов. For the ASCII range of characters, UTF-8 is identical to ASCII encoding and allows a broader set of characters. Однако для китайской, японской и корейской письменности UTF-8 может потребовать три байта для каждого символа, что может привести к большему объему данных по сравнению с UTF-16. However, for Chinese-Japanese-Korean (CJK) scripts, UTF-8 can require three bytes for each character, and can potentially cause larger data sizes than UTF-16. Обратите внимание, что иногда увеличение размера данных для китайской, японской и корейской письменности объясняется объемом данных ASCII, например тегами HTML. Note that sometimes the amount of ASCII data, such as HTML tags, justifies the increased size for the CJK range.
UTF-16 UTF-16 UnicodeEncoding Представляет каждую кодовую точку Юникода в виде последовательности одного или двух 16-разрядных целых чисел. Represents each Unicode code point as a sequence of one or two 16-bit integers. Наиболее распространенные символы Юникода требуют только одной кодовой точки UTF-16, хотя дополнительные символы Юникода (U+10000 и далее) требуют двух замещающих кодовых точек UTF-16. Most common Unicode characters require only one UTF-16 code point, although Unicode supplementary characters (U+10000 and greater) require two UTF-16 surrogate code points. Поддерживаются оба порядка байтов: прямой и обратный. Both little-endian and big-endian byte orders are supported. Кодировка UTF-16 используется средой CLR для представления значений Char и String , а операционной системой Windows — для представления значений WCHAR . UTF-16 encoding is used by the common language runtime to represent Char and String values, and it is used by the Windows operating system to represent WCHAR values.
UTF-32 UTF-32 UTF32Encoding Представляет каждую кодовую точку Юникода в виде 32-разрядного целого числа. Represents each Unicode code point as a 32-bit integer. Поддерживаются оба порядка байтов: прямой и обратный. Both little-endian and big-endian byte orders are supported. Кодировка UTF-32 используется в случае, когда приложению требуется избежать поведения замещающей кодовой точки кодировки UTF-16 в операционных системах, в которых закодированное пространство имеет большое значение. UTF-32 encoding is used when applications want to avoid the surrogate code point behavior of UTF-16 encoding on operating systems for which encoded space is too important. Для кодирования отдельных отображаемых глифов может использоваться несколько символов UTF-32. Single glyphs rendered on a display can still be encoded with more than one UTF-32 character.
Кодировки ANSI и ISO ANSI/ISO encodings Предоставляет поддержку ряда кодовых страниц. Provides support for a variety of code pages. В операционных системах Windows кодовые страницы используются для поддержки конкретного языка или группы языков. On Windows operating systems, code pages are used to support a specific language or group of languages. Таблицу кодовых страниц, поддерживаемых платформой .NET, можно найти в описании класса Encoding. For a table that lists the code pages supported by .NET, see the Encoding class. Чтобы получить объект кодировки для конкретной кодовой страницы, можно вызвать метод Encoding.GetEncoding(Int32) . You can retrieve an encoding object for a particular code page by calling the Encoding.GetEncoding(Int32) method. Кодовая страница содержит 256 кодовых точек с отсчетом от нуля. A code page contains 256 code points and is zero-based. В большинстве кодовых страниц кодовые точки от 0 до 127 представляют набор символов ASCII, а кодовые точки от 128 до 255 существенно отличаются у разных кодовых страниц. In most code pages, code points 0 through 127 represent the ASCII character set, and code points 128 through 255 differ significantly between code pages. Например, кодовая страница 1252 предоставляет символы для латинских систем письма, включая английский, немецкий и французский языки. For example, code page 1252 provides the characters for Latin writing systems, including English, German, and French. Последние 128 кодовых точек на кодовой странице 1252 содержат диакритические знаки. The last 128 code points in code page 1252 contain the accent characters. Кодовая страница 1253 содержит коды символов, которые требуются в греческой системе письма. Code page 1253 provides character codes that are required in the Greek writing system. Последние 128 кодовых точек на кодовой странице 1253 содержат символы греческого языка. The last 128 code points in code page 1253 contain the Greek characters. Таким образом, в приложении, использующем кодовые страницы ANSI, нельзя хранить греческий и немецкий тексты в одном потоке, если он не содержит идентификатор, указывающий соответствующую кодовую страницу. As a result, an application that relies on ANSI code pages cannot store Greek and German in the same text stream unless it includes an identifier that indicates the referenced code page.
Двухбайтовые кодировки (DBCS) Double-byte character set (DBCS) encodings Поддерживают языки, такие как китайский, японский и корейский, содержащие более 256 символов. Supports languages, such as Chinese, Japanese, and Korean, that contain more than 256 characters. В кодировке DBCS каждый символ представлен парой кодовых точек (два байта). In a DBCS, a pair of code points (a double byte) represents each character. Свойство Encoding.IsSingleByte возвращает значение false для двухбайтовых кодировок. The Encoding.IsSingleByte property returns false for DBCS encodings. Чтобы получить объект кодировки для конкретной кодировки DBCS, можно вызвать метод Encoding.GetEncoding(Int32) . You can retrieve an encoding object for a particular DBCS by calling the Encoding.GetEncoding(Int32) method. В кодировке DBCS каждый символ представлен парой кодовых точек (два байта). In a DBCS, a pair of code points (a double byte) represents each character. Когда приложение обрабатывает данные DBCS, первый байт символа DBCS (старший байт) обрабатывается в сочетании со вторым байтом, следующим непосредственно за ним. When an application handles DBCS data, the first byte of a DBCS character (the lead byte) is processed in combination with the trail byte that immediately follows it. Так как одна пара двухбайтовых кодовых точек может представлять различные символы в зависимости от кодовой страницы, эта схема также не позволяет использовать в одном потоке данных два языка, например японский и китайский. Because a single pair of double-byte code points can represent different characters depending on the code page, this scheme still does not allow for the combination of two languages, such as Japanese and Chinese, in the same data stream.

Эти кодировки позволяют работать с символами Юникода, а также с кодировками, которые часто используются в приложениях прежних версий. These encodings enable you to work with Unicode characters as well as with encodings that are most commonly used in legacy applications. Кроме того, можно создать настраиваемую кодировку, определив класс, производный от Encoding , и переопределив его члены. In addition, you can create a custom encoding by defining a class that derives from Encoding and overriding its members.

Заметки о платформе: .NET Core Platform Notes: .NET Core

По умолчанию .NET Core не предоставляет доступ к кодировкам кодовых страниц, кроме кодовой страницы 28591 и кодировок Юникода, например UTF-8 и UTF-16. By default, .NET Core does not make available any code page encodings other than code page 28591 and the Unicode encodings, such as UTF-8 and UTF-16. Но вы можете добавить в свое приложение кодировки кодовых страниц из стандартных приложений Windows, ориентированных на .NET. However, you can add the code page encodings found in standard Windows apps that target .NET to your app. Подробнее см. в разделе CodePagesEncodingProvider . For complete information, see the CodePagesEncodingProvider topic.

Выбор класса кодировки Selecting an Encoding Class

Если у вас есть возможность выбрать кодировку для использования в приложении, следует использовать Юникод, предпочтительно UTF8Encoding или UnicodeEncoding. If you have the opportunity to choose the encoding to be used by your application, you should use a Unicode encoding, preferably either UTF8Encoding or UnicodeEncoding. (Также .NET поддерживает третью кодировку Юникода: UTF32Encoding.) (.NET also supports a third Unicode encoding, UTF32Encoding.)

Если вы планируете использовать кодировку ASCII (ASCIIEncoding), выберите вместо нее UTF8Encoding . If you are planning to use an ASCII encoding (ASCIIEncoding), choose UTF8Encoding instead. Эти две кодировки идентичны для набора символов ASCII, но UTF8Encoding имеет указанные ниже преимущества. The two encodings are identical for the ASCII character set, but UTF8Encoding has the following advantages:

Она может представлять любой символ Юникода, тогда как ASCIIEncoding поддерживает только символы Юникода в диапазоне от U+0000 до U+007F. It can represent every Unicode character, whereas ASCIIEncoding supports only the Unicode character values between U+0000 and U+007F.

Она обеспечивает обнаружение ошибок и более высокий уровень безопасности. It provides error detection and better security.

Она настроена для максимально быстрой работы и должна быть быстрее любых других кодировок. It has been tuned to be as fast as possible and should be faster than any other encoding. Даже для содержимого, имеющего только формат ASCII, выполнение операций с помощью UTF8Encoding происходит быстрее, чем с помощью ASCIIEncoding. Even for content that is entirely ASCII, operations performed with UTF8Encoding are faster than operations performed with ASCIIEncoding.

Кодировку ASCIIEncoding рекомендуется использовать только для приложений прежних версий. You should consider using ASCIIEncoding only for legacy applications. Однако даже для приложений прежних версий UTF8Encoding может быть предпочтительнее по указанным ниже причинам (при параметрах по умолчанию). However, even for legacy applications, UTF8Encoding might be a better choice for the following reasons (assuming default settings):

Если содержимое приложения включает символы не только в формате ASCII, при кодировании с помощью ASCIIEncodingкаждый символ, не относящийся к ASCII, кодируется как знак вопроса (?). If your application has content that is not strictly ASCII and encodes it with ASCIIEncoding, each non-ASCII character encodes as a question mark (?). При последующем декодировании эти данные утрачиваются. If the application then decodes this data, the information is lost.

Если содержимое приложения включает символы не только в формате ASCII, при кодировании с помощью UTF8Encodingпредставление символов в формате ASCII дает непригодный для чтения результат. If your application has content that is not strictly ASCII and encodes it with UTF8Encoding, the result seems unintelligible if interpreted as ASCII. Однако при последующем декодировании данных с помощью декодера UTF-8 обработка данных выполняется успешно. However, if the application then uses a UTF-8 decoder to decode this data, the data performs a round trip successfully.

В веб-приложении символы, отправленные клиенту в ответ на веб-запрос, должны отражать кодировку, используемую в клиенте. In a web application, characters sent to the client in response to a web request should reflect the encoding used on the client. Как правило, требуется задать для свойства HttpResponse.ContentEncoding значение, возвращаемое свойством HttpRequest.ContentEncoding , для отображения текста в той кодировке, которую ожидает пользователь. In most cases, you should set the HttpResponse.ContentEncoding property to the value returned by the HttpRequest.ContentEncoding property to display text in the encoding that the user expects.

Использование объекта кодировки Using an Encoding Object

Кодировщик преобразует строку символов (чаще всего символов Юникода) в их числовой (байтовый) эквивалент. An encoder converts a string of characters (most commonly, Unicode characters) to its numeric (byte) equivalent. Например, кодировщик ASCII можно использовать для преобразования символов Юникода в ASCII, чтобы они могли отображаться на консоли. For example, you might use an ASCII encoder to convert Unicode characters to ASCII so that they can be displayed at the console. Чтобы выполнить преобразование, вызовите метод Encoding.GetBytes . To perform the conversion, you call the Encoding.GetBytes method. Если перед выполнением кодирования нужно определить, сколько байтов потребуется для хранения закодированных символов, можно вызвать метод GetByteCount . If you want to determine how many bytes are needed to store the encoded characters before performing the encoding, you can call the GetByteCount method.

В примере ниже один массив байтов используется для кодирования строк в двух отдельных операциях. The following example uses a single byte array to encode strings in two separate operations. Имеется индекс, указывающий начальную позицию в массиве байтов для следующего набора байтов, закодированных с использованием ASCII. It maintains an index that indicates the starting position in the byte array for the next set of ASCII-encoded bytes. Вызывается метод ASCIIEncoding.GetByteCount(String) для проверки того, что массив байтов достаточно велик для хранения закодированной строки. It calls the ASCIIEncoding.GetByteCount(String) method to ensure that the byte array is large enough to accommodate the encoded string. Затем вызывается метод ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) для кодирования символов в строке. It then calls the ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) method to encode the characters in the string.

Декодер преобразует массив байтов, отражающий конкретную кодировку символов, в набор символов в массиве символов или в строке. A decoder converts a byte array that reflects a particular character encoding into a set of characters, either in a character array or in a string. Чтобы декодировать массив байтов в массив символов, вызовите метод Encoding.GetChars . To decode a byte array into a character array, you call the Encoding.GetChars method. Чтобы декодировать массив байтов в строку, вызовите метод GetString . To decode a byte array into a string, you call the GetString method. Если перед декодированием нужно определить, сколько символов требуется для хранения раскодированных байтов, можно вызвать метод GetCharCount . If you want to determine how many characters are needed to store the decoded bytes before performing the decoding, you can call the GetCharCount method.

В примере ниже три строки кодируются, а затем декодируются в один массив символов. The following example encodes three strings and then decodes them into a single array of characters. Имеется индекс, указывающий начальную позицию в массиве символов для следующего набора декодированных символов. It maintains an index that indicates the starting position in the character array for the next set of decoded characters. Вызывается метод GetCharCount для проверки того, что массив символов достаточно велик для хранения всех декодированных символов. It calls the GetCharCount method to ensure that the character array is large enough to accommodate all the decoded characters. Затем вызывается метод ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) для декодирования массива байтов. It then calls the ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) method to decode the byte array.

Методы кодирования и декодирования класса, производного от класса Encoding , предназначены для работы с полным набором данных. Это значит, что все данные, подлежащие кодированию или декодированию, предоставляются в одном вызове метода. The encoding and decoding methods of a class derived from Encoding are designed to work on a complete set of data; that is, all the data to be encoded or decoded is supplied in a single method call. Однако в некоторых случаях данные предоставляются в потоке, тогда данные для кодирования и декодирования можно получить только с помощью нескольких операций чтения. However, in some cases, data is available in a stream, and the data to be encoded or decoded may be available only from separate read operations. В таком случае необходимо, чтобы операция кодирования или декодирования «помнила» сохраненное после предыдущего вызова состояние. This requires the encoding or decoding operation to remember any saved state from its previous invocation. Методы классов, производных от Encoder и Decoder , могут обрабатывать операции кодирования и декодирования, охватывающие несколько вызовов методов. Methods of classes derived from Encoder and Decoder are able to handle encoding and decoding operations that span multiple method calls.

Объект Encoder для конкретной кодировки доступен в ее свойстве Encoding.GetEncoder . An Encoder object for a particular encoding is available from that encoding’s Encoding.GetEncoder property. Объект Decoder для конкретной кодировки доступен в ее свойстве Encoding.GetDecoder . A Decoder object for a particular encoding is available from that encoding’s Encoding.GetDecoder property. Что касается операций декодирования, обратите внимание, что классы, производные от Decoder , включают метод Decoder.GetChars , но не имеют метода, соответствующего Encoding.GetString. For decoding operations, note that classes derived from Decoder include a Decoder.GetChars method, but they do not have a method that corresponds to Encoding.GetString.

В примере ниже показано различие между использованием методов Encoding.GetChars и Decoder.GetChars для декодирования массива байтов Юникода. The following example illustrates the difference between using the Encoding.GetChars and Decoder.GetChars methods for decoding a Unicode byte array. В этом примере строка, содержащая несколько символов Юникода, кодируется в файл, а затем два метода декодирования используются для декодирования по десять байтов за раз. The example encodes a string that contains some Unicode characters to a file, and then uses the two decoding methods to decode them ten bytes at a time. Так как замещающая пара оказывается в десятом и одиннадцатом байтах, она декодируется в отдельных вызовах метода. Because a surrogate pair occurs in the tenth and eleventh bytes, it is decoded in separate method calls. Как видно из выходных данных, метод Encoding.GetChars не может правильно декодировать байты и заменяет их символом U+FFFD (замещающим символом). As the output shows, the Encoding.GetChars method is not able to correctly decode the bytes and instead replaces them with U+FFFD (REPLACEMENT CHARACTER). С другой стороны, метод Decoder.GetChars может успешно декодировать массив байтов для получения исходной строки. On the other hand, the Decoder.GetChars method is able to successfully decode the byte array to get the original string.

Выбор резервной стратегии Choosing a Fallback Strategy

Когда метод пытается закодировать или декодировать символ, но не находит сопоставления, он должен использовать резервную стратегию, определяющую, как должно обрабатываться отсутствие сопоставления. When a method tries to encode or decode a character but no mapping exists, it must implement a fallback strategy that determines how the failed mapping should be handled. Существует три типа резервных стратегий: There are three types of fallback strategies:

Best-Fit Fallback Best-fit fallback

Replacement Fallback Replacement fallback

Exception Fallback Exception fallback

При операциях кодирования наиболее часто проблемы возникают, когда символ Юникода не удается сопоставить с определенной кодировкой кодовой страницы. The most common problems in encoding operations occur when a Unicode character cannot be mapped to a particular code page encoding. При операциях декодирования наиболее часто проблемы возникают, когда недопустимую последовательность байтов не удается преобразовать в допустимые символы Юникода. The most common problems in decoding operations occur when invalid byte sequences cannot be translated into valid Unicode characters. Поэтому необходимо знать, какую резервную стратегию использует определенный объект кодировки. For these reasons, you should know which fallback strategy a particular encoding object uses. По возможности при создании экземпляра объекта следует указывать резервную стратегию, используемую объектом кодировки. Whenever possible, you should specify the fallback strategy used by an encoding object when you instantiate the object.

Best-Fit Fallback Best-Fit Fallback

Если символ не имеет точного соответствия в целевой кодировке, кодировщик может попытаться сопоставить его с похожим символом. When a character does not have an exact match in the target encoding, the encoder can try to map it to a similar character. (Стратегия наилучшего соответствия связана с проблемами, возникающими скорее при кодировании, чем при декодировании. (Best-fit fallback is mostly an encoding rather than a decoding issue. Существует лишь небольшое число кодовых страниц, символы которых нельзя сопоставить с Юникодом.) Стратегия наилучшего соответствия является стратегией по умолчанию для кодовых страниц и двухбайтовых кодировок, извлекаемых перегрузками Encoding.GetEncoding(Int32) и Encoding.GetEncoding(String). There are very few code pages that contain characters that cannot be successfully mapped to Unicode.) Best-fit fallback is the default for code page and double-byte character set encodings that are retrieved by the Encoding.GetEncoding(Int32) and Encoding.GetEncoding(String) overloads.

Теоретически все классы кодировок Юникода, доступные в .NET (UTF8Encoding, UnicodeEncoding и UTF32Encoding), поддерживают все символы всех наборов символов, поэтому при их использовании не возникает проблем со стратегией наилучшего соответствия. In theory, the Unicode encoding classes provided in .NET (UTF8Encoding, UnicodeEncoding, and UTF32Encoding) support every character in every character set, so they can be used to eliminate best-fit fallback issues.

Для разных кодовых страниц применяются разные стратегии наилучшего соответствия. Best-fit strategies vary for different code pages. Например, для некоторых кодовых страниц полноширинные латинские символы сопоставляются с более распространенными полуширинными символами. For example, for some code pages, full-width Latin characters map to the more common half-width Latin characters. Для других кодовых страниц такое сопоставление не выполняется. For other code pages, this mapping is not made. Даже в случае применения активной стратегии наилучшего соответствия для некоторых символов некоторых кодировок отсутствует возможное сопоставление. Even under an aggressive best-fit strategy, there is no imaginable fit for some characters in some encodings. Например, для идеографических символов китайского алфавита отсутствуют корректные сопоставления с символами кодовой страницы 1252. For example, a Chinese ideograph has no reasonable mapping to code page 1252. В этом случае используются замещающие строки. In this case, a replacement string is used. По умолчанию в качестве замещающей строки используется знак вопроса (U+003F). By default, this string is just a single QUESTION MARK (U+003F).

Стратегии наилучшего соответствия не документированы подробно. Best-fit strategies are not documented in detail. Но для некоторых кодовых страниц их можно найти на веб-сайте Unicode Consortium. However, several code pages are documented at the Unicode Consortium’s website. Изучите файл readme.txt в предоставленной папке, где описаны правила интерпретации для файлов сопоставления. Please review the readme.txt file in that folder for a description of how to interpret the mapping files.

В примере ниже используется кодовая страница 1252 (кодовая страница Windows для западноевропейских языков) для иллюстрации стратегии наилучшего соответствия и ее недостатков. The following example uses code page 1252 (the Windows code page for Western European languages) to illustrate best-fit mapping and its drawbacks. Метод Encoding.GetEncoding(Int32) используется для получения объекта кодировки для кодовой страницы 1252. The Encoding.GetEncoding(Int32) method is used to retrieve an encoding object for code page 1252. По умолчанию для неподдерживаемых символов Юникода используется стратегия наилучшего соответствия. By default, it uses a best-fit mapping for Unicode characters that it does not support. В примере создается экземпляр строки, содержащий три символа, не относящихся к ASCII (прописная латинская буква S в кружке (U+24C8), надстрочный индекс 5 (U+2075) и знак бесконечности (U+221E)), разделенные пробелами. The example instantiates a string that contains three non-ASCII characters — CIRCLED LATIN CAPITAL LETTER S (U+24C8), SUPERSCRIPT FIVE (U+2075), and INFINITY (U+221E) — separated by spaces. Как видно из выходных данных примера, при кодировке строки три исходных отличных от пробела символа заменяются вопросительным знаком (U+003F), цифрой пять (U+0035) и цифрой восемь (U+0038). As the output from the example shows, when the string is encoded, the three original non-space characters are replaced by QUESTION MARK (U+003F), DIGIT FIVE (U+0035), and DIGIT EIGHT (U+0038). Цифра восемь — особенно неудачная замена неподдерживаемого знака бесконечности, а вопросительный знак показывает, что для исходного символа сопоставление не найдено. DIGIT EIGHT is a particularly poor replacement for the unsupported INFINITY character, and QUESTION MARK indicates that no mapping was available for the original character.

По умолчанию для объекта Encoding применяется стратегия наилучшего соответствия, при которой данные в формате Юникод кодируются в формат кодовой страницы. Ряд приложений предыдущих версий построен с учетом этой стратегии. Best-fit mapping is the default behavior for an Encoding object that encodes Unicode data into code page data, and there are legacy applications that rely on this behavior. Однако в целях безопасности в большинстве новых приложений не рекомендуется применять эту стратегию. However, most new applications should avoid best-fit behavior for security reasons. Например, приложениям не следует выполнять кодировку доменного имени в режиме наилучшего соответствия. For example, applications should not put a domain name through a best-fit encoding.

Вы также можете реализовать пользовательскую стратегию наилучшего соответствия для кодировки. You can also implement a custom best-fit fallback mapping for an encoding. Дополнительные сведения см. в разделе Implementing a Custom Fallback Strategy . For more information, see the Implementing a Custom Fallback Strategy section.

Если стратегия наилучшего соответствия задана по умолчанию для объекта кодировки, вы можете выбрать другую резервную стратегию при извлечении объекта Encoding с помощью вызова перегрузки Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) или Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) . If best-fit fallback is the default for an encoding object, you can choose another fallback strategy when you retrieve an Encoding object by calling the Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) or Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) overload. В следующем разделе приводится пример, в котором каждый символ, который не удается сопоставить с кодовой страницей 1252, заменяется звездочкой (*). The following section includes an example that replaces each character that cannot be mapped to code page 1252 with an asterisk (*).

Replacement Fallback Replacement Fallback

Когда символ не имеет точного соответствия в целевой схеме и нет подходящего символа, с которым его можно сопоставить, приложение может использовать замещающий символ или строку. When a character does not have an exact match in the target scheme, but there is no appropriate character that it can be mapped to, the application can specify a replacement character or string. Так по умолчанию поступает декодер Юникода, заменяющий любую двухбайтовую последовательность, которую он не может декодировать, замещающим символом (U+FFFD). This is the default behavior for the Unicode decoder, which replaces any two-byte sequence that it cannot decode with REPLACEMENT_CHARACTER (U+FFFD). Кроме того, это поведение по умолчанию класса ASCIIEncoding , который заменяет каждый символ, который не удается кодировать или декодировать, вопросительным знаком. It is also the default behavior of the ASCIIEncoding class, which replaces each character that it cannot encode or decode with a question mark. В примере ниже показана замена символов для строки Юникода из предыдущего примера. The following example illustrates character replacement for the Unicode string from the previous example. Как видно из выходных данных, каждый символ, который не удается декодировать в байтовое значение ASCII, заменяется 0x3F, то есть кодом ASCII для вопросительного знака. As the output shows, each character that cannot be decoded into an ASCII byte value is replaced by 0x3F, which is the ASCII code for a question mark.

В .NET есть классы EncoderReplacementFallback и DecoderReplacementFallback, которые подставляют замещающую строку, если не удается точно сопоставить символ при кодировании или декодировании. .NET includes the EncoderReplacementFallback and DecoderReplacementFallback classes, which substitute a replacement string if a character does not map exactly in an encoding or decoding operation. По умолчанию эта замещающая строка — вопросительный знак, но вы можете вызвать перегрузку конструктора класса, чтобы выбрать другую строку. By default, this replacement string is a question mark, but you can call a class constructor overload to choose a different string. Как правило, замещающая строка — это отдельный символ, хотя это необязательно. Typically, the replacement string is a single character, although this is not a requirement. В примере ниже поведение кодировщика кодовой страницы 1252 изменяется путем создания экземпляра объекта EncoderReplacementFallback , который использует символ звездочки (*) в качестве замещающей строки. The following example changes the behavior of the code page 1252 encoder by instantiating an EncoderReplacementFallback object that uses an asterisk (*) as a replacement string.

Также можно реализовать класс замены для кодировки. You can also implement a replacement class for an encoding. Дополнительные сведения см. в разделе Implementing a Custom Fallback Strategy . For more information, see the Implementing a Custom Fallback Strategy section.

Помимо вопросительного знака (U+003F) в качестве замещающей строки часто используется замещающий символ Юникода (U+FFFD), особенно при декодировании последовательностей байтов, которые не удается преобразовать в символы Юникода. In addition to QUESTION MARK (U+003F), the Unicode REPLACEMENT CHARACTER (U+FFFD) is commonly used as a replacement string, particularly when decoding byte sequences that cannot be successfully translated into Unicode characters. Однако вы можете выбрать любую замещающую строку, в том числе из нескольких символов. However, you are free to choose any replacement string, and it can contain multiple characters.

Exception Fallback Exception Fallback

Вместо подстановки наиболее подходящей или замещающей строки кодировщик может создавать исключение EncoderFallbackException , если не удается закодировать набор символов, а декодер — создавать исключение DecoderFallbackException , если не удается декодировать массив байтов. Instead of providing a best-fit fallback or a replacement string, an encoder can throw an EncoderFallbackException if it is unable to encode a set of characters, and a decoder can throw a DecoderFallbackException if it is unable to decode a byte array. Для создания исключения в операциях кодирования и декодирования методу EncoderExceptionFallback необходимо предоставить объект DecoderExceptionFallback или Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) соответственно. To throw an exception in encoding and decoding operations, you supply an EncoderExceptionFallback object and a DecoderExceptionFallback object, respectively, to the Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) method. В примере ниже иллюстрируется резервная стратегия исключения с классом ASCIIEncoding . The following example illustrates exception fallback with the ASCIIEncoding class.

Вы также можете реализовать пользовательский обработчик исключений для операции кодирования. You can also implement a custom exception handler for an encoding operation. Дополнительные сведения см. в разделе Implementing a Custom Fallback Strategy . For more information, see the Implementing a Custom Fallback Strategy section.

Объекты EncoderFallbackException и DecoderFallbackException предоставляют следующую информацию о состоянии, вызвавшем исключение: The EncoderFallbackException and DecoderFallbackException objects provide the following information about the condition that caused the exception:

Объект EncoderFallbackException включает метод IsUnknownSurrogate , указывающий, представляют ли символы (или символ), которые не удается закодировать, неизвестную замещающую пару (тогда метод возвращает значение true ) или неизвестный отдельный символ (тогда метод возвращает значение false ). The EncoderFallbackException object includes an IsUnknownSurrogate method, which indicates whether the character or characters that cannot be encoded represent an unknown surrogate pair (in which case, the method returns true ) or an unknown single character (in which case, the method returns false ). Символы замещающей пары доступны в свойствах EncoderFallbackException.CharUnknownHigh и EncoderFallbackException.CharUnknownLow . The characters in the surrogate pair are available from the EncoderFallbackException.CharUnknownHigh and EncoderFallbackException.CharUnknownLow properties. Неизвестный отдельный символ доступен в свойстве EncoderFallbackException.CharUnknown . The unknown single character is available from the EncoderFallbackException.CharUnknown property. Свойство EncoderFallbackException.Index указывает позицию первого символа, который не удалось закодировать, в строке. The EncoderFallbackException.Index property indicates the position in the string at which the first character that could not be encoded was found.

Объект DecoderFallbackException включает свойство BytesUnknown , возвращающее массив байтов, которые не удается декодировать. The DecoderFallbackException object includes a BytesUnknown property that returns an array of bytes that cannot be decoded. Свойство DecoderFallbackException.Index указывает начальную позицию неизвестных байтов. The DecoderFallbackException.Index property indicates the starting position of the unknown bytes.

Несмотря на то что объекты EncoderFallbackException и DecoderFallbackException предоставляют достаточно подробную диагностическую информацию об исключении, они не предоставляют доступ к буферу кодирования или декодирования. Although the EncoderFallbackException and DecoderFallbackException objects provide adequate diagnostic information about the exception, they do not provide access to the encoding or decoding buffer. Поэтому они не позволяют заменять или исправлять недопустимые данные в методе кодирования или декодирования. Therefore, they do not allow invalid data to be replaced or corrected within the encoding or decoding method.

Implementing a Custom Fallback Strategy Implementing a Custom Fallback Strategy

Помимо встроенной стратегии наилучшего соответствия, реализованной кодовыми страницами, платформа .NET содержит следующие классы для реализации резервной стратегии: In addition to the best-fit mapping that is implemented internally by code pages, .NET includes the following classes for implementing a fallback strategy:

— классы EncoderReplacementFallback и EncoderReplacementFallbackBuffer можно использовать для замены символов в операциях кодирования; Use EncoderReplacementFallback and EncoderReplacementFallbackBuffer to replace characters in encoding operations.

— классы DecoderReplacementFallback и DecoderReplacementFallbackBuffer можно использовать для замены символов в операциях декодирования; Use DecoderReplacementFallback and DecoderReplacementFallbackBuffer to replace characters in decoding operations.

— классы EncoderExceptionFallback и EncoderExceptionFallbackBuffer можно использовать для создания исключения EncoderFallbackException , когда символ не удается закодировать; Use EncoderExceptionFallback and EncoderExceptionFallbackBuffer to throw an EncoderFallbackException when a character cannot be encoded.

— классы DecoderExceptionFallback и DecoderExceptionFallbackBuffer можно использовать для создания исключения DecoderFallbackException , когда символ не удается декодировать. Use DecoderExceptionFallback and DecoderExceptionFallbackBuffer to throw a DecoderFallbackException when a character cannot be decoded.

Кроме того, можно реализовать пользовательское решение, использующее резервную стратегию наилучшего соответствия или стратегию исключения, выполнив указанные ниже действия. In addition, you can implement a custom solution that uses best-fit fallback, replacement fallback, or exception fallback, by following these steps:

Создайте класс, производный от EncoderFallback , для операций кодирования и класс, производный от DecoderFallback , для операций декодирования. Derive a class from EncoderFallback for encoding operations, and from DecoderFallback for decoding operations.

Создайте класс, производный от EncoderFallbackBuffer , для операций кодирования и класс, производный от DecoderFallbackBuffer , для операций декодирования. Derive a class from EncoderFallbackBuffer for encoding operations, and from DecoderFallbackBuffer for decoding operations.

Для задания резервной стратегии исключения, если классы EncoderFallbackException и DecoderFallbackException не отвечают вашим требованиям, следует наследовать класс от объекта исключения, например Exception или ArgumentException. For exception fallback, if the predefined EncoderFallbackException and DecoderFallbackException classes do not meet your needs, derive a class from an exception object such as Exception or ArgumentException.

Наследование от класса EncoderFallback или класса DecoderFallback Deriving from EncoderFallback or DecoderFallback

Для реализации пользовательской резервной стратегии необходимо создать класс, наследующий от EncoderFallback для операций кодирования и от класса DecoderFallback для операций декодирования. To implement a custom fallback solution, you must create a class that inherits from EncoderFallback for encoding operations, and from DecoderFallback for decoding operations. Экземпляры этих классов передаются в метод Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) и служат посредниками между классом кодировки и реализацией резервной стратегии. Instances of these classes are passed to the Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) method and serve as the intermediary between the encoding class and the fallback implementation.

При создании пользовательской резервной стратегии для кодировщика или декодера необходимо реализовать следующие члены: When you create a custom fallback solution for an encoder or decoder, you must implement the following members:

— свойство EncoderFallback.MaxCharCount или DecoderFallback.MaxCharCount , возвращающее максимально возможное число символов, которое может использоваться для замены одного символа в стратегиях наилучшего соответствия, замены или исключения. The EncoderFallback.MaxCharCount or DecoderFallback.MaxCharCount property, which returns the maximum possible number of characters that the best-fit, replacement, or exception fallback can return to replace a single character. Для пользовательской резервной стратегии исключения его значение равно нулю. For a custom exception fallback, its value is zero.

Метод EncoderFallback.CreateFallbackBuffer или DecoderFallback.CreateFallbackBuffer , возвращающий пользовательскую реализацию EncoderFallbackBuffer или DecoderFallbackBuffer . The EncoderFallback.CreateFallbackBuffer or DecoderFallback.CreateFallbackBuffer method, which returns your custom EncoderFallbackBuffer or DecoderFallbackBuffer implementation. Метод вызывается кодировщиком, когда он встречает первый символ, который не удается закодировать, или декодером, когда он встречает первый байт, который не удается декодировать. The method is called by the encoder when it encounters the first character that it is unable to successfully encode, or by the decoder when it encounters the first byte that it is unable to successfully decode.

Наследование от класса EncoderFallbackBuffer или класса DecoderFallbackBuffer Deriving from EncoderFallbackBuffer or DecoderFallbackBuffer

Для реализации пользовательской резервной стратегии необходимо также создать класс, наследующий от EncoderFallbackBuffer для операций кодирования и от класса DecoderFallbackBuffer для операций декодирования. To implement a custom fallback solution, you must also create a class that inherits from EncoderFallbackBuffer for encoding operations, and from DecoderFallbackBuffer for decoding operations. Экземпляры этих классов возвращаются методом CreateFallbackBuffer классов EncoderFallback и DecoderFallback . Instances of these classes are returned by the CreateFallbackBuffer method of the EncoderFallback and DecoderFallback classes. Метод EncoderFallback.CreateFallbackBuffer вызывается кодировщиком, когда он встречает первый символ, который не удается закодировать, а метод DecoderFallback.CreateFallbackBuffer вызывается декодером, когда он встречает один или несколько байтов, которые не удается декодировать. The EncoderFallback.CreateFallbackBuffer method is called by the encoder when it encounters the first character that it is not able to encode, and the DecoderFallback.CreateFallbackBuffer method is called by the decoder when it encounters one or more bytes that it is not able to decode. Классы EncoderFallbackBuffer и DecoderFallbackBuffer предоставляют реализацию резервной стратегии. The EncoderFallbackBuffer and DecoderFallbackBuffer classes provide the fallback implementation. Каждый экземпляр представляет буфер, содержащий символы резервной стратегии, которые заменят символ, который не удалось закодировать, или последовательность байтов, которую не удалось декодировать. Each instance represents a buffer that contains the fallback characters that will replace the character that cannot be encoded or the byte sequence that cannot be decoded.

При создании пользовательской резервной стратегии для кодировщика или декодера необходимо реализовать следующие члены: When you create a custom fallback solution for an encoder or decoder, you must implement the following members:

метод EncoderFallbackBuffer.Fallback или DecoderFallbackBuffer.Fallback . The EncoderFallbackBuffer.Fallback or DecoderFallbackBuffer.Fallback method. МетодEncoderFallbackBuffer.Fallback вызывается кодировщиком, чтобы предоставить резервный буфер со сведениями о символе, который не удается кодировать. EncoderFallbackBuffer.Fallback is called by the encoder to provide the fallback buffer with information about the character that it cannot encode. Так как символ, который требуется закодировать, может быть замещающей парой, этот метод перегружается. Because the character to be encoded may be a surrogate pair, this method is overloaded. Одной перегрузке передается символ, который нужно закодировать, и его индекс в строке. One overload is passed the character to be encoded and its index in the string. Второй перегрузке передаются верхний и нижний замещающий знаки и их индекс в строке. The second overload is passed the high and low surrogate along with its index in the string. Метод DecoderFallbackBuffer.Fallback вызывается декодером, чтобы предоставить резервный буфер со сведениями о байтах, которые не удается декодировать. The DecoderFallbackBuffer.Fallback method is called by the decoder to provide the fallback buffer with information about the bytes that it cannot decode. Этому методу передается массив байтов, которые не удалось декодировать, а также индекс первого байта. This method is passed an array of bytes that it cannot decode, along with the index of the first byte. Метод резервной стратегии должен возвращать значение true , если резервный буфер может предоставить наилучшим образом соответствующий или замещающий символ (или символы); в противном случае он должен возвращать значение false . The fallback method should return true if the fallback buffer can supply a best-fit or replacement character or characters; otherwise, it should return false . При использовании стратегии исключения метод резервной стратегии должен создавать исключение. For an exception fallback, the fallback method should throw an exception.

Метод EncoderFallbackBuffer.GetNextChar или DecoderFallbackBuffer.GetNextChar , который вызывается кодировщиком или декодером многократно для получения следующего символа из резервного буфера. The EncoderFallbackBuffer.GetNextChar or DecoderFallbackBuffer.GetNextChar method, which is called repeatedly by the encoder or decoder to get the next character from the fallback buffer. После возврата всех резервных символов метод должен вернуть символ U+0000. When all fallback characters have been returned, the method should return U+0000.

Свойство EncoderFallbackBuffer.Remaining или DecoderFallbackBuffer.Remaining , которое возвращает количество символов, оставшихся в резервном буфере. The EncoderFallbackBuffer.Remaining or DecoderFallbackBuffer.Remaining property, which returns the number of characters remaining in the fallback buffer.

Метод EncoderFallbackBuffer.MovePrevious или DecoderFallbackBuffer.MovePrevious , который перемещает текущую позицию в резервном буфере к предыдущему символу. The EncoderFallbackBuffer.MovePrevious or DecoderFallbackBuffer.MovePrevious method, which moves the current position in the fallback buffer to the previous character.

Метод EncoderFallbackBuffer.Reset или DecoderFallbackBuffer.Reset , повторно инициализирующий резервный буфер. The EncoderFallbackBuffer.Reset or DecoderFallbackBuffer.Reset method, which reinitializes the fallback buffer.

Если реализована резервная стратегия наилучшего соответствия или замены, классы, унаследованные от EncoderFallbackBuffer и DecoderFallbackBuffer , также имеют два закрытых поля экземпляра: точное число символов в буфере и индекс в буфере следующего символа, который нужно вернуть. If the fallback implementation is a best-fit fallback or a replacement fallback, the classes derived from EncoderFallbackBuffer and DecoderFallbackBuffer also maintain two private instance fields: the exact number of characters in the buffer; and the index of the next character in the buffer to return.

Пример EncoderFallback An EncoderFallback Example

В примере выше использовалась стратегия замены для замены символов Юникода, не соответствующих символам ASCII, звездочкой (*). An earlier example used replacement fallback to replace Unicode characters that did not correspond to ASCII characters with an asterisk (*). В примере ниже используется пользовательская резервная стратегия наилучшего соответствия для получения более удачного сопоставления символов, отсутствующих в ASCII. The following example uses a custom best-fit fallback implementation instead to provide a better mapping of non-ASCII characters.

В приведенном коде определяется класс с именем CustomMapper , производный от EncoderFallback , для обработки наилучшего сопоставления символов, отсутствующих в ASCII. The following code defines a class named CustomMapper that is derived from EncoderFallback to handle the best-fit mapping of non-ASCII characters. Его метод CreateFallbackBuffer возвращает объект CustomMapperFallbackBuffer , предоставляющий реализацию EncoderFallbackBuffer . Its CreateFallbackBuffer method returns a CustomMapperFallbackBuffer object, which provides the EncoderFallbackBuffer implementation. Класс CustomMapper использует объект Dictionary для хранения сопоставлений неподдерживаемых символов Юникода (значение ключа) и соответствующих им 8-битных символов (которые хранятся в двух последовательных байтах в виде 64-разрядного целого числа). The CustomMapper class uses a Dictionary object to store the mappings of unsupported Unicode characters (the key value) and their corresponding 8-bit characters (which are stored in two consecutive bytes in a 64-bit integer). Чтобы это сопоставление было доступно резервному буферу, экземпляр CustomMapper передается в качестве параметра конструктору класса CustomMapperFallbackBuffer . To make this mapping available to the fallback buffer, the CustomMapper instance is passed as a parameter to the CustomMapperFallbackBuffer class constructor. Так как самое длинное сопоставление — это строка INF для символа Юникода с кодом U+221E, свойство MaxCharCount возвращает значение 3. Because the longest mapping is the string «INF» for the Unicode character U+221E, the MaxCharCount property returns 3.

В примере кода ниже определяется класс CustomMapperFallbackBuffer , производный от EncoderFallbackBuffer. The following code defines the CustomMapperFallbackBuffer class, which is derived from EncoderFallbackBuffer. Словарь, содержащий сопоставления наилучшего соответствия и определенный в экземпляре класса CustomMapper , доступен из конструктора класса. The dictionary that contains best-fit mappings and that is defined in the CustomMapper instance is available from its class constructor. Его метод Fallback возвращает значение true , если какие-либо символы Юникода, которые не удается кодировать кодировщику ASCII, определены в словаре сопоставлений; в противном случае возвращается значение false . Its Fallback method returns true if any of the Unicode characters that the ASCII encoder cannot encode are defined in the mapping dictionary; otherwise, it returns false . Для каждого резервного действия закрытая переменная count указывает число символов, которые осталось вернуть, а закрытая переменная index указывает позицию в буфере строк (значение charsToReturn ) следующего символа, который нужно вернуть. For each fallback, the private count variable indicates the number of characters that remain to be returned, and the private index variable indicates the position in the string buffer, charsToReturn , of the next character to return.

Java против C#: какой язык производительнее в реальных проектах?

Сегодня мы познакомимся с одним опытом, при помощи которого усердный автор, Джефф Когсвелл, попытался рассмотреть производительность кода Java и C# «в реальных условиях». Этот опыт можно считать приглашением к анализу производительности двух языков, ведь полученные автором результаты не претендуют на абсолютную объективность. Но, учитывая соперничество и популярность затронутых в статье языков, надеемся, что материал станет для читателя вкусной пищей для размышлений.

Автор подготовил специальные тесты и решил проверить, какой из этих языков окажется лучше «в реальных условиях»

Давайте сравним Java и C# — два языка программирования, каждый из которых имеет широкий круг поклонников, но вместе с тем и яростных противников. Несмотря на все те прения, которые разворачиваются в онлайне, сложно найти четкие критерии, позволяющие оценить реальную производительность каждого из этих языков.

Какой смысл я вкладываю в понятие «реальный»? Дело в том, что меня не интересует еще один тест, скрупулезно вычисляющий значение числа Пи до миллиона знаков. Я хочу поговорить о решении прикладных задач на языке: какой язык мне выбрать, если требуется ежедневно выдавать клиентам миллионы веб-страниц? Какой из языков сильнее при выборке информации из базы данных и динамической сборке веб-страниц? Именно такая статистика обычно интересует технического специалиста, выбирающего подходящую платформу.

Прежде чем перейти к тестам, давайте определимся с терминологией. Когда вы пишете код Java, вы обычно планируете использовать его на виртуальной машине Java (JVM). Иными словами, ваш код компилируется в байт-код, а этот байт-код работает под управлением JVM. C#, в свою очередь, обычно работает в общеязыковой исполняющей среде (CLR) от Microsoft. C#, как и Java, компилируется в байт-код.

Java и C# — это просто языки. Теоретически вы могли бы писать код Java для исполняющей среды Microsoft CLR, а также код C# для JVM. Действительно, на работу с виртуальной машиной Java ориентирован и ряд других языков, в частности Erlang, Python и др. Самые распространенные языки, рассчитанные на работу с CLR (кроме C#), — собственный язык Microsoft Visual Basic.NET, а также майкрософтовская разновидность C++, называемая C++.NET. Общеязыковая исполняющая среда также поддерживает некоторые менее распространенные языки — уже упомянутый выше Python и F#.

Две эти исполняющие среды содержат фреймворки, представляющие собой наборы классов. Такие наборы для JVM были написаны в Oracle/Sun, а для CLR — в Microsoft. У Oracle есть платформа Java с разнообразными API. Фреймворк Microsoft .NET — это огромный набор классов, обеспечивающих разработку для CLR. На самом деле, многие специалисты называют всю систему просто .NET, а не CLR.

Мы не будем сравнивать языки как таковые. Нас интересуют лежащие в их основе исполняющие среды. Но мы заинтересованы не только и не столько быстродействием этих сред, сколько производительностью фреймворков. Поэтому я выполню несколько сравнений, но в итоге нам все равно придется соотнести яблоки с яблоками.

Например, вы вполне можете написать собственный HTTP-слушатель на C# или Java, а потом просто отправить клиенту динамически сгенерированную HTML-страницу. Но на практике почти никто не пишет низкоуровневых HTTP-слушателей; обычно мы стремимся использовать имеющиеся HTTP-серверы. Большинство веб-приложений на C# работают на базе майкрософтовского сервера IIS.

C другой стороны, серверный код на Java может работать с несколькими разными серверами, в частности Apache HTTP и Tomcat. Кстати, сервер Tomcat был специально разработан для взаимодействия с серверным кодом Java. Мы, конечно, хотели бы сравнивать сопоставимые величины, но в то же время должны сохранить реалистичность эксперимента. Скорее всего, отклик будет зависеть от сервера, а одни серверы работают быстрее других. Хотя HTTP-серверы технически и не входят в состав исполняющей среды, они применяются практически всегда, поэтому их производительность нельзя не учитывать. В первом тесте мы обойдемся без этих стандартных инструментов, а напишем собственные небольшие HTTP-серверы. Во втором случае мы опробуем подобные тесты с аналогичными HTTP-серверами, чтобы получить более точную и полную картину.

Еще одна проблема — это статические файлы, я собираюсь обойтись в данном опыте без них. Некоторые читатели могут со мной не согласиться, но при современных архитектурах, если вам требуется высокая скорость работы со статическими файлами, написанными на JavaScript или CSS, их просто можно загрузить на облачный сервер, данные которого тиражируются во всей стране. Далее по DNS-конфигурации определяем, какой сервер расположен ближе всего к клиенту, и отсылаем данные весьма быстро. Именно поэтому я собираюсь пропустить эту часть. Вдобавок, если вас интересует максимальная производительность, вы не будете нагружать ваше веб-приложение выдачей статических файлов, а постараетесь выполнять в нем только самую необходимую работу: считывание баз данных, сборка динамического контента и т. п.

Замечание об аппаратном обеспечении

Я хочу гарантировать, что применяемое в тестах оборудование привносит в опыт минимальное количество посторонних переменных факторов. На той машине, где я занимаюсь разработкой, стоит масса дополнительных программ, в частности многочисленные сервисы, которые запускаются автоматически и отхватывают процессорное время. В идеале следовало бы выделить под процесс Java или C# целое процессорное ядро, но, к сожалению, выделение ядер происходит иначе. Вы можете ограничить зону действия процесса одним ядром, но не можете «не допустить» в это ядро другие процессы. Поэтому я выделяю для опыта крупные серверы на Amazon EC2, системы которых можно считать базовыми. Поскольку здесь мы не собираемся сравнивать Linux и Windows, а C# ориентирован преимущественно на Windows (если не учитывать проект Mono, который мы и не будем учитывать), все тесты будут выполнены в Windows.

С клиентской стороны требовалось обеспечить, чтобы результаты не искажались из-за сетевых задержек. Единственная задержка могла испортить итоги всего теста. Поэтому я решил исполнять клиентский и серверный код на одной и той же машине. Итак, я не могу «заставить» операционную систему выделять по ядру на процесс, но могу заключить каждый процесс в своем ядре, что я и сделал.

Сбор результатов

Результаты хронометрировались на клиентской стороне. Делая это, лучше всего отследить время и сохранить его, потом по мере необходимости снова отмечать время, и так до конца теста. Лишь по окончании работы выполняются все подсчеты. Кроме того, я не выводил на консоль никаких данных до полного завершения теста. Распространенная ошибка, которую совершают в подобных тестах: в определенных точках отбираются значения времени, в каждой точке подсчитывается разница во времени, после чего результаты выводятся на консоль. Консоли работают медленно, особенно если их требуется прокручивать. Итак, работаем до конца, а потом вычисляем разностные значения и выводим данные на экран.

Клиентский код

Фактически неважно, какой код мы используем в качестве клиентского — важно последовательно задействовать его во всех тестах. Клиентский код будет имитировать работу браузера и измерять, сколько времени требуется на доставку страницы с сервера. Для этого можно использовать C# или Java. Я остановился на C#, поскольку в нем есть очень простой класс WebClient и несложный класс-таймер.

Первый тест: слушание HTTP

Начнем. Мы протестируем код, который просто открывает HTTP-слушатель и рассылает динамически сгенерированные веб-страницы.

Сначала попробуем Java. Мы можем реализовать описанную задачу несколькими способами, но я хотел бы обратить внимание на два подхода. Во-первых, попробуем открыть слушатель TCP/IP на порте 80 и дождаться входящих соединений. Это очень низкоуровневый метод, при котором мы будем пользоваться классом Socket. Другой интересующий нас вариант — использование класса HttpServer. Вот почему я собираюсь воспользоваться этим классом: если мы действительно хотим сравнить скорость Java и C#, без участия Веба, то можно применить некоторые базовые индикаторы, не связанные с работой в Интернете. Так, можно написать два консольных приложения, которые будут оперировать подборкой математических уравнений и, возможно, также выполнять кое-какой строковый поиск и конкатенацию — но это уже другая история. Здесь нас интересует Веб, поэтому займемся HttpServer и его эквивалентом на C#.

Сразу же я обнаружил одну аномалию: выполнение любого запроса в Java-версии длится почти в 2000 раз больше. На обработку 5 запросов при получении строки из CLR-программы, использующей класс HttpListener, ушло около 17 615 тактов процессора, а на 5 аналогичных запросов с применением сервера Java и класса HttpListener было израсходовано 7 882 975 тактов. Если выразить это соотношение в миллисекундах, то имеем 2 миллисекунды на 15 запросов на сервере C# и 4045 миллисекунд на сервере Java.

Добавив на сервер Java некоторую отладочную информацию, я выяснил, что сама функция, занятая ответами на входящие запросы и рассылкой данных, работает довольно быстро — ни разу не приближаясь даже к показателю в 3 секунды. Вероятно, узкое место расположено где-то во фреймворке Java, при отправке данных обратно к клиенту. Но такая проблема отсутствует при обмене информацией с клиентом C#.

Чтобы докопаться до сути проблемы, я решил перейти на другой Java-клиент. Отказался от сравнительно тяжеловесного класса HttpServer, а взамен создал простой сокет, слушающий TCP/IP — для этого воспользовался классом ServerSocket. Вручную создал строку заголовка и основной текст, совпадающий с отправленным в версию на C#.

Ситуация значительно улучшилась. Могу запускать множество тестов; выполняю 2000 запросов один за другим, но не собираю данных о времени, пока не завершатся все 2000 вызовов к серверу Java. Потом осуществляю аналогичный процесс с сервером C#. В данном случае время измеряется в миллисекундах. На 2000 запросов к серверу Java уходит 2687 миллисекунд. На 2000 запросов к серверу на C# тратится 214 миллисекунд. C# по-прежнему гораздо быстрее.

Поскольку сохраняется такая значительная разница, мне ничего не оставалось, кроме как испробовать версию Java на сервере Linux. Я воспользовался сервером «c1.medium» на Amazon EC2. Установил оба упомянутых класса Java и получил фактически такие же скорости. Класс HttpServer тратит около 14 секунд на обработку 15 запросов. Плоховато.

И, наконец, чтобы быть абсолютно уверенным, я написал на Java эквивалентную клиентскую программу, получающую данные. Временные показатели существенно не изменились.

Второй тест: полнофункциональный сайт

Как я уже указывал, мы редко пользуемся самодельными HTTP-серверами. Программисты, работающие с C#, обычно прибегают к IIS. У приверженцев Java есть несколько вариантов, в частности, Tomcat. В моих тестах я использовал именно эти два сервера. В варианте с C# я задействовал платформу ASP.NET MVC 4, работающую на IIS 8. Применил два метода: в первом случае возвращал HTML-строку от самого контроллера; во втором — возвращал представление, содержащее справку даты/времени.

В тестах Java можно применять два похожих метода. Можно работать с сервлетом, возвращающим информацию на HTML, либо возвращать результаты на странице JSP. Эти методы аналогичны приемам C#, в первом из которых задействуется контроллер, а во втором — представление. Можно было бы применить более новые Java Faces или любые другие фреймворки; оставляю эти задачи всем заинтересованным для самостоятельного изучения.

Контроллер C# просто возвращает HTML-строку. При прогоне моего клиентского теста с 2000 итераций на него уходит 991 миллисекунда. Опять же, гораздо быстрее, чем версия с сокетом Java.

Та версия приложения C#, которая работает с представлением, создает полнофункциональную HTML-страницу, соответствующую всем стандартам. Здесь есть элементы HTML, head, meta, title, body и внутренний элемент div, содержащий текст «The date and time is» с указанием даты и времени. Дату и время мы получаем в экземпляре DateTime.Now, динамически записывая эту информацию при каждом запросе.

Прогон клиентского теста (2000 итераций) в такой версии с представлением занимает 1804 миллисекунды; примерно вдвое дольше, чем напрямую. Напрямую мы возвращаем более краткий HTML, но если увеличить HTML до размеров, сопоставимых с вариантом-представлением, разница практически отсутствует; длительность колеблется в пределах 950—1000 миллисекунд. Даже при добавлении динамической записи даты и времени процесс существенно не замедляется. В любых условиях версия с представлением выполняется примерно вдвое дольше, чем версия с контроллером.

Перейдем к Java. Сервлет не сложнее, чем контроллер C#. Он просто возвращает строку, содержащую HTML-страницу. На возврат 2000 экземпляров уходит 479 миллисекунд. Это примерно вдвое быстрее, чем с контроллером C# — действительно впечатляет.

Возврат JSP-страницы также происходит очень быстро. Как и в случае с C#, второй вариант протекает дольше первого. В данном случае на возврат 2000 экземпляров расходуется 753 миллисекунды. Если добавить к JSP-файлу вызов, возвращающий дату, заметной разницы не возникает. На самом деле, на сервере Tomcat явно выполняется какая-то оптимизация, так как в последующих попытках на возврат 2000 экземпляров тратится уже 205 миллисекунд.

Заключение

Эти результаты кажутся мне довольно интересными. Я много лет профессионально занимался программированием на C#, мне неоднократно говорили, ссылаясь на личный опыт, что .NET — одна из самых быстрых существующих сред исполнения. Но эти тесты свидетельствуют об обратном. Разумеется, они минимальны; я не делал никаких крупных вычислений, активных запросов к базе данных. Возможно, я еще проведу тесты с базой данных и уточню результаты. Но в моем опыте Java побеждает с явным преимуществом.

10 фич в C#, о которых вы определённо должны узнать и начать их использовать

Если вы только начали изучение C# или же решили расширить свои знания, мы нашли для вас 10 фич, знание которых позволит вам избежать ошибок, писать более понятный код и сохранить кучу времени.

1. async / await

Использование паттернов async / await позволяет разблокировать UI / текущий поток во время выполнения блочных операторов. Паттерны async / await позволяют коду продолжить выполнение, даже если что-то блокирует его выполнение (например, веб-запрос).

2. Инициализаторы объектов / массивов / коллекций

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

3. Лямбды, предикаты, делегаты и замыкания

Эти фичи просто незаменимы во многих случаях: например, при использовании LINQ. Поэтому рекомендуем вам убедиться, что вы действительно понимаете, когда и как их использовать.

4. ?? (Оператор объединения с NULL)

x ?? y — возвращает x , если значение отличается от null ; в противном случае возвращает y .

Может быть несколько операторов .

?? также может быть использован для перевода типов null в не null :

5. $”” (Интерполяция строк) — C# 6

Фича в C# 6 позволяет эффективно и элегантно собирать строки:

6. ?.(определяет null) — C# 6

x?.y — доступ к членам, определяемый условием null . Возвращает значение null , если левый операнд имеет значение null .

Больше никаких NullReferenceExceptions!

7. Выражение nameof — C# 6

Может показаться, что выражение nameof не особо полезно, но это не так. При использовании автоматических инструментов рефакторинга (например, ReSharper) иногда может потребоваться обратиться к аргументу метода по его имени:

Вот, как это должно быть:

8. Инициализаторы свойств (property) — C# 6

Инициализаторы свойств позволяют задавать начальные значения для свойств:

Польза их использования заключается в том, что вы не можете объявить setter, тем самым делая свойства неизменяемыми. Инициализаторы свойств хорошо работают в связке с синтаксисом первичного конструктора в C# 6.

9. Операторы as и is

Is — совместимость типов. Возвращает значение true, если вычисленный левый операнд может быть приведен к типу, указанному в правом операнде (статический тип).

As — преобразование типов. Возвращает левый операнд, приведенный к типу, заданному правым операндом (статический тип), но as возвращает null , где (T)x вызывает исключение.

10. Ключевое слово yield

Ключевое слово yield позволяет заполнить интерфейс IEnumerable объектами (items). Следующий пример вернет все степени двойки от 2 до 2 в степени 8 (то есть 2, 4, 8, 16, 32, 128, 256):

В правильных руках yield — очень мощная вещь. Оно позволяет с легкостью генерировать последовательность объектов, то есть системе не придется перечислять всю коллекцию целиком — это может быть сделано по требованию.

Дмитpий Hecтepук

Блог о программировании — C#, F#, C++, архитектура, и многое другое

C++ для Java и C# разработчиков

Думаю мы все можем согласиться с идеей о том, что С++ разработчику достаточть легко в последствии освоить C#, Java или еще какой-то более современный язык. Но вот обратное — не совсем так. Многие разработчики начинают плеваться как только слышат про «ручное управление памятью» и подобные вещи. Суть этого поста — попытаться донести для разработчиков во-первых зачем им нужен С++, ну и показать как можно на нем делать те же вещи, к которым они возможно привыкли в других языка. А также объективно рассказать про те проблемы, которые в С++ до сих пор не решены.

Зачем нужен С++

Сегодня существуют три основных индустрии, которым нужен С++. Это

В играх нужна производительность, поэтому С++ помогает ее достичь будучи ближе к железу чем языки основанные на виртуальных машинах. В С++ можно работать с памятью напрямую и даже вставлять в код куски Assembler’а.

В разработке для встроенных устройств тоже важна производительность, а также там ограничения по памяти которые порой не дают развернуть управляемую среду. Поэтому С и С++ все еще там популярны, хотя были и есть попытки запускать и C# (.NET) и Java на таких устройствах.

У нас в quant finance, С++ остался по историческим причинам. Финансовая индустрия весьма консервативна, а современные веяния высокочастотной торговли требуют максимум производительности. На уровне рассчетов моделей есть популярная библиотека QuantLib, но большинство алгоритмов пишут in-house, хотя тоже часто на С++.

Помимо производительности, есть еще несколько причин писать на С++. Одна из них это портативность (внезапно, да?) — на С++ вполне можно писать полностью портативный код. Я вот например использую Intel C++ Compiler (по фичам он «не очень»), который существует как на Windows так и на Linux.

Еще одна причина использовать С++ заключается в том, что это единственный способ амортизировать мощные аппаратные платформы, такие как CUDA и Intel Xeon Phi. Но это в основном для тех кто любит гоняться за пиковой производительностью.

Целочисленные типы

Когда я пишу в C# что-то вроде int x; я четко знаю две вещи:

Переменная x – это 32-битное целое число со знаком

Изначально, x содержит значение 0 (ноль)

В С++, мы не можем быть уверены ни в одном из этих утверждений. Тип int не определен с той строгостью какой бы хотелось (подразумевалось, что на других платформах — например embedded — у него может быть другой размер), а его дефолтное значение в большинстве случаев не определено, и там может быть что угодно.

Для решения первой проблемы, можно использовать С++ заголовок , который определяет такие типы как uint8_t , int32_t и так далее. Это существенно улучшает портативность кода.

Вторая проблема толком не решена, поэтому вам придется писать int32_t x<0>; . К счастью, в С++ с последнее время наконец-то можно инициализировать поля класса прямо в теле, вот так:

Статические поля так инициализировать, к сожалению, все ещё нельзя.

Строки

Со строками в С++ беда. Во-первых, изначально в С++, как и в С, строка была просто указателем на массив байтов (да-да, байтов а не code point’ов) с нулевым байтом \0 на конце. Это значит что вычисление длины строки — это O(n) операция.

Далее в C++ появился тип string , но с ним есть масса проблем. Например, у этого типа есть функции size() и length() , которые делают одно и тоже, но из API это совсем не очевидно. Но еще обиднее что там нет таких очевидных вещей как to_lower/upper() или split() , в результате чего приходится пользоватся сторонними библиотеками вроде Boost.

С++ весьма амбивалентентен в плане поддержки Unicode. Под Windows, кодировка строки типа string — ANSI, а вовсе не UTF-8. Есть также тип wstring , который использует UTF16 (ту же кодировку, что C# и Java).

Массивы

Изначально, массивы достались С++ из С, и это означает что массив — это просто указатель на первый элемент, и всё. То есть массив сам не знает какой он длины, и чтобы написать функцию которая обрабатывает массив, нужно 2 аргумента: указатель и длина.

Сейчас у нас есть такие типы как array и vector . Эти типы позволяют хранить либо массивы фиксированной длины (например array ) или, в случае с vector , иметь динамический массив, который расширяется и сужается по мере добавления в него элементов.

Многомерные массивы в С++ (как и в С) тоже выглядят плохо, т.к. являются всего лишь массивами указателей. Для них тоже стоит использовать array/vector , а еще лучше, если требуются вычисления, использовать специализированную библиотеку вроде Eigen.

Выделение памяти

Адресное пространство поделено на условно 2 области — стэк и куча. Стэк предназначен для временных данных, и имеет ограничения по размеру, в то время как куча — это ваша оперативная память, занимайте хоть всю.

В связи с этим, в отличии от языков со «сборкой мусора», в С++ есть два способа выделения (аллокации) памяти: на стэке и в куче.

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

Другое дело — выделение в куче. Она для более долгосрочного хранения данных, и ответственность за очистку этой памяти лежит на вас. Если забудете, будет «утечка памяти», что очень плохо.

Как видите, для выделения в куче, мы используем оператор new , и он нам возвращает указатель на выделенную память. Чтобы освободить эту память, нужно использовать оператор delete .

Значения, указатели и ссылки

Давайте начнем с опеределений. Вот есть у вас переменная int32_t x<42>; — под нее выделено 4 байта, и каждый раз когда вы «берете» x , вы получаете значение 42 (ну, пока вы его не поменяли). Запись x = 11; запишет в эту память значение 11.

У нас также есть такая штука как указатель — это переменная, которая содержит адрес другой переменной. То есть можно написать вот так:

Тип этой переменной — со звездочкой, и звездочка означает что это указатель. Ее значение — это адрес переменной x , а чтобы взять адрес чего-либо в С++ нужно посдавить амперсанд, вот так: &x .

Хитрость тут в том, что через адрес переменной можно манипулировать самой переменной. То есть вместо x = 42 можно написать *y = 42 . Заметьте что тут звездочка стоит перед именем переменной, и означает «следовать за». То есть мы идем туда, куда указывает y и в том месте памяти меняем значение.

Наконец, третий способ доступа к переменной — это ссылка. Ссылка действует так же как и указатель, только синтаксис другой. Вот смотрите:

В отличии от указателей, тип ссылки заканчивается на & , и ссылке не требуется адрес для присваивания — можно просто подсунуть ей саму переменную. Для доступа по ссылке, в отличии от указателей, тоже не нужно никакого префикса: можно просто использовать ссылку как обычную переменную.

Также, в отличии от C#/Java, в С++ ссылка не может быть пустой (null). Это значит что все ссылки должны быть инициализированы, т.е. ссылаться на что-то. Это с одной стороны хорошо (в управляемых языках многие мечтают о non-nullable objects), но с другой стороны создает много проблем — например, если в классе есть поле-ссылка, вы обязаны инициализировать ее в конструкторе.

Умные указатели

К сожалению, ни один из типов приведенных ранее не ведет себя как ссылка в C# или Java, т.к. любой объект, выделенный в куче, нужно удалять вручную. Но как это сделать если ты не знаешь, кому, когда и на сколько времени понадобится твой объект? Для этого есть умные указатели.

Умный указатель содержит в себе указатель на объект в куче и счетчик использований. Как только счетчик доходит до нуля, объект уничтожается. Выглядит это вот как-то так:

Такой объект можно передавать куда угодно и не бояться его использовать, и он — пожалуй лучший аналог ссылок в C#/Java, хотя и с ограничениями. Одно из ограничений — невозможность рекурсии, то есть нельзя писать вот так:

Поскольку нет никакого централизованного учета ссылок, подобная конструкция не приведет ни к чему хорошему — в Child вместо shared_ptr придется использовать weak_ptr ну или обычную ссылку.

shared_ptr<> также решает проблему хранения ссылок в таких коллекциях как vector . Проблема в том, что нельзя сделать vector , но вполне можно сделать vector > .

Помимо shared_ptr<> есть несколько других типов умных указателей.

Алгоритмы

С++ не обладает никаким «интерфейсом» который символизирует перечисляемые объекты. Вместо этого он использует итераторы, которые умеют ходить по той или иной структуре. Любой перечисляемый тип может определить функции begin() и end() который возвращают итераторы на первый и за-последним элементы.

С++ также определяет цикл for для обхода коллекций, то есть можно написать:

Код выше также использует ключевое слово auto (аналог var в C#) для вывода типов.

В целом, алгоритмы в С++ это глобальные функции с такими красочными названиями как count_if и всякими прелестями вроде того факта, что remove на контейнере вовсе не удаляет объекты из контейнера, а только сваливает их в конец, а собственно удаляет их функция erase() (очень неинтуитивно).

У контейнеров нет функций-членов с алгоритмами вроде sort() или search() . И добавить их в контейнер нельзя (за исключением наследования) т.к. в С++ нет функций расширения. Алгоритмы специально спроектированы так, чтобы работать на всех возможных контейнерах путем итерирования.

Заключение

Сейчас С++ становится лучше. Улучшается синтаксис языка, добавляются новые библиотеки, но стоит признать, что некоторые проблемы так и не решены, и есть множество недоработок в стандартной библиотеке, некоторые из которых кроме как переписыванием не починить.

В плане компиляторов тоже все лучше и лучше. Есть компилятор Clang (дает вменяемые ошибки при компиляции вместо километров ада), интерпретатор Cling (REPL-среда на базе Clang, рекомендую!), C++ компилятор от Microsoft поддерживает инкрементальную и Edit & Continue компиляцию, а еще скоро процесс компиляции существенно ускорится путем введения модулей.

Что касается инструментов, то ReSharper C++ (если вы пишете под Visual Studio) и CLion (кросс-платформенная IDE) позволяют комфортно писать на этом языке. А такие технологии как CUDA Toolkit и Intel Parallel Studio позволяют амортизировать популярные аппаратные платформы CUDA и Intel Xeon Phi. ■

Язык программирования c#. 6

Общее описания языка c#. 6

Современные архиваторы.………………………………………………. 11

Разработка программного продукта . ………………………..…………….16

Реализация пользовательского интерфейса………………………………. 21

Список использованных источников………………………………. 27

Целью курсовой работы является разработка архиватора методом сжатия RLE (run-length encoding) с применение объектно-ориентированных возможностей языка программирования C# в среде разработки приложений Microsoft Visual Studio 2010 Ultimate.

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

1 Язык программирования с#

1.1 Общее описание языка с#

Последнее время С и С++ являются наиболее используемыми языками для разработки коммерческих и бизнес приложений. Эти языки устраивают многих разработчиков, но в действительности не обеспечивают должной продуктивности разработки. К примеру, процесс написания приложения на С++ зачастую занимает значительно больше времени, чем разработка эквивалентного приложения, скажем, на Visual Basic. Сейчас существуют языки, увеличивающие продуктивность разработки за счет потери в гибкости, которая так привычна и необходима программистам на С/С++. Подобные решения являются весьма неудобными для разработчиков и зачастую предлагают значительно меньшие возможности. Эти языки также не ориентированы на взаимодействие с появляющимися сегодня системами и очень часто они не соответствуют существующей практике программирования для Web. Многие разработчики хотели бы использовать современный язык, который позволял бы писать, читать и сопровождать программы с простотой Visual Basic и в то же время давал мощь и гибкость C++, обеспечивал доступ ко всем функциональным возможностям системы, взаимодействовал бы с существующими программами и легко работал с возникающими Web стандартами.

Учитывая все подобные пожелания, Microsoft разработала новый язык — C#. В него входит много полезных особенностей — простота, объектная ориентированность, типовая защищенность, «сборка мусора», поддержка совместимости версий и многое другое. Данные возможности позволяют быстро и легко разрабатывать приложения, особенно COM+ приложения и Web сервисы. При создании C#, его авторы учитывали достижения многих других языков программирования: C++, C, Java, SmallTalk, Delphi, Visual Basic и т.д. Надо заметить что по причине того, что C# разрабатывался с чистого листа, у его авторов была возможность (которой они явно воспользовались), оставить в прошлом все неудобные и неприятные особенности (существующие, как правило, для обратной совместимости), любого из предшествующих ему языков. В результате получился действительно простой, удобный и современный язык, по мощности не уступающий С++, но существенно повышающий продуктивность разработок.

Очень часто можно проследить такую связь — чем более язык защищен и устойчив к ошибкам, тем меньше производительность программ, написанных на нем. К примеру рассмотрим две крайности — очевидно это Assembler и Java. В первом случае вы можете добиться фантастической быстроты своей программы, но вам придется очень долго заставлять ее работать правильно не на вашем компьютере. В случае же с Java — вы получаете защищенность, независимость от платформы, но, к сожалению, скорость вашей программы вряд ли совместима со сложившимся представлением о скорости, например, какого-либо отдельного клиентского приложения (конечно существуют оговорки — JIT компиляция и прочее).

В C#, как в несомненно современном языке, также существуют характерные особенности для обхода возможных ошибок. Например, помимо упомянутой выше «сборки мусора», там все переменные автоматически инициализируются средой и обладают типовой защищенностью, что позволяет избежать неопределенных ситуаций в случае, если программист забудет инициализировать переменную в объекте или попытается произвести недопустимое преобразование типов. Также в C# были предприняты меры для исключения ошибок при обновлении программного обеспечения. Изменение кода, в такой ситуации, может непредсказуемо изменить суть самой программы. Чтобы помочь разработчикам бороться с этой проблемой C# включает в себя поддержку совместимости версий (vesioning). В частности, в отличии от C++ и Java, если метод класса был изменен, это должно быть специально оговорено. Это позволяет обойти ошибки в коде и обеспечить гибкую совместимость версий. Также новой особенностью является native поддержка интерфейсов и наследования интерфейсов. Данные возможности позволяют разрабатывать сложные системы и развивать их со временем.

В C# была унифицирована система типов, теперь вы можете рассматривать каждый тип как объект. Несмотря на то, используете вы класс, структуру, массив или встроенный тип, вы можете обращаться к нему как к объекту. Объекты собраны в пространства имен (namespaces), которые позволяют программно обращаться к чему-либо. Это значит что вместо списка включаемых файлов заголовков в своей программе вы должны написать какие пространства имен, для доступа к объектам и классам внутри них, вы хотите использовать. В C# выражение using позволяет вам не писать каждый раз название пространства имен, когда вы используете класс из него. Например, пространство имен System содержит несколько классов, в том числе и Console. И вы можете писать либо название пространства имен перед каждым обращением к классу, либо использовать using как это было показано в примере выше.

Важной и отличительной от С++ особенностью C# является его простота. К примеру, всегда ли вы помните, когда пишите на С++, где нужно использовать «->», где «::», а где «.»? Даже если нет, то компилятор всегда поправляет вас в случае ошибки. Это говорит лишь о том, что в действительности можно обойтись только одним оператором, а компилятор сам будет распознавать его значение. Так в C#, оператор»->» используется очень ограничено (в unsafe блоках, о которых речь пойдет ниже), оператор «::» вообще не существует. Практически всегда вы используете только оператор «.» и вам больше не нужно стоять перед выбором.

Еще один пример. При написании программ на C/С++ вам приходилось думать не только о типах данных, но и о их размере в конкретной реализации. В C# все упрощено — теперь символ Unicode называется просто char (а не wchar_t, как в С++) и 64-битное целое теперь — long (а не __int64). Также в C# нет знаковых и беззнаковых символьных типов.

В C#, также как и в Visual Basic после каждого выражения case в блоке switch подразумевается break. И более не будет происходить странных вещей если вы забыли поставить этот break. Однако если вы действительно хотите чтобы после одного выражения case программа перешла к следующему вы можете переписать свою программу с использованием, например, оператора goto.

Многим программистам (на тот момент, наверное, будущим программистам) было не так легко во время изучения C++ полностью освоиться с механизмом ссылок и указателей. В C# (кто-то сейчас вспомнит о Java) нет указателей. В действительности нетривиальность указателей соответствовала их полезности. Например, порой, трудно себе представить программирование без указателей на функции. В соответствии с этим в C# присутствуют Delegates — как прямой аналог указателя на функцию, но их отличает типовая защищенность, безопасность и полное соответствие концепциям объектно-ориентированного программирования.

Хотелось бы подчеркнуть современное удобство C#. Когда вы начнете работу с C#, а, надеюсь, это произойдет как можно скорее, вы увидите, что довольно большое значение в нем имеют пространства имен. Уже сейчас, на основе первого примера, вы можете судить об этом — ведь все файлы заголовков заменены именно пространством имен. Так в C#, помимо просто выражения using, предоставляется еще одна очень удобная возможность — использование дополнительного имени (alias) пространства имен или класса.

Современность C# проявляется и в новых шагах к облегчению процесса отладки программы. Традиционным средством для отладки программ на стадии разработки в C++ является маркировка обширных частей кода директивами #ifdef и т.д. В C#, используя атрибуты, ориентированные на условные слова, вы можете куда быстрее писать отлаживаемый код.

В наше время, когда усиливается связь между миром коммерции и миром разработки программного обеспечения, и корпорации тратят много усилий на планирование бизнеса, ощущается необходимость в соответствии абстрактных бизнес процессов их программным реализациям. К сожалению, большинство языков реально не имеют прямого пути для связи бизнес логики и кода. Например, сегодня многие программисты комментируют свои программы для объяснения того, какие классы реализуют какой-либо абстрактный бизнес объект. C# позволяет использовать типизированные, расширяемые метаданные, которые могут быть прикреплены к объекту. Архитектурой проекта могут определяться локальные атрибуты, которые будут связанны с любыми элементами языка — классами, интерфейсами и т.д. Разработчик может программно проверить атрибуты какого-либо элемента. Это существенно упрощает работу, к примеру, вместо того чтобы писать автоматизированный инструмент, который будет проверять каждый класс или интерфейс, на то, является ли он действительно частью абстрактного бизнес объекта, можно просто воспользоваться сообщениями основанными на определенных в объекте локальных атрибутах.

Дмитpий Hecтepук

Блог о программировании — C#, F#, C++, архитектура, и многое другое

C++ для Java и C# разработчиков

Думаю мы все можем согласиться с идеей о том, что С++ разработчику достаточть легко в последствии освоить C#, Java или еще какой-то более современный язык. Но вот обратное — не совсем так. Многие разработчики начинают плеваться как только слышат про «ручное управление памятью» и подобные вещи. Суть этого поста — попытаться донести для разработчиков во-первых зачем им нужен С++, ну и показать как можно на нем делать те же вещи, к которым они возможно привыкли в других языка. А также объективно рассказать про те проблемы, которые в С++ до сих пор не решены.

Зачем нужен С++

Сегодня существуют три основных индустрии, которым нужен С++. Это

В играх нужна производительность, поэтому С++ помогает ее достичь будучи ближе к железу чем языки основанные на виртуальных машинах. В С++ можно работать с памятью напрямую и даже вставлять в код куски Assembler’а.

В разработке для встроенных устройств тоже важна производительность, а также там ограничения по памяти которые порой не дают развернуть управляемую среду. Поэтому С и С++ все еще там популярны, хотя были и есть попытки запускать и C# (.NET) и Java на таких устройствах.

У нас в quant finance, С++ остался по историческим причинам. Финансовая индустрия весьма консервативна, а современные веяния высокочастотной торговли требуют максимум производительности. На уровне рассчетов моделей есть популярная библиотека QuantLib, но большинство алгоритмов пишут in-house, хотя тоже часто на С++.

Помимо производительности, есть еще несколько причин писать на С++. Одна из них это портативность (внезапно, да?) — на С++ вполне можно писать полностью портативный код. Я вот например использую Intel C++ Compiler (по фичам он «не очень»), который существует как на Windows так и на Linux.

Еще одна причина использовать С++ заключается в том, что это единственный способ амортизировать мощные аппаратные платформы, такие как CUDA и Intel Xeon Phi. Но это в основном для тех кто любит гоняться за пиковой производительностью.

Целочисленные типы

Когда я пишу в C# что-то вроде int x; я четко знаю две вещи:

Переменная x – это 32-битное целое число со знаком

Изначально, x содержит значение 0 (ноль)

В С++, мы не можем быть уверены ни в одном из этих утверждений. Тип int не определен с той строгостью какой бы хотелось (подразумевалось, что на других платформах — например embedded — у него может быть другой размер), а его дефолтное значение в большинстве случаев не определено, и там может быть что угодно.

Для решения первой проблемы, можно использовать С++ заголовок , который определяет такие типы как uint8_t , int32_t и так далее. Это существенно улучшает портативность кода.

Вторая проблема толком не решена, поэтому вам придется писать int32_t x<0>; . К счастью, в С++ с последнее время наконец-то можно инициализировать поля класса прямо в теле, вот так:

Статические поля так инициализировать, к сожалению, все ещё нельзя.

Строки

Со строками в С++ беда. Во-первых, изначально в С++, как и в С, строка была просто указателем на массив байтов (да-да, байтов а не code point’ов) с нулевым байтом \0 на конце. Это значит что вычисление длины строки — это O(n) операция.

Далее в C++ появился тип string , но с ним есть масса проблем. Например, у этого типа есть функции size() и length() , которые делают одно и тоже, но из API это совсем не очевидно. Но еще обиднее что там нет таких очевидных вещей как to_lower/upper() или split() , в результате чего приходится пользоватся сторонними библиотеками вроде Boost.

С++ весьма амбивалентентен в плане поддержки Unicode. Под Windows, кодировка строки типа string — ANSI, а вовсе не UTF-8. Есть также тип wstring , который использует UTF16 (ту же кодировку, что C# и Java).

Массивы

Изначально, массивы достались С++ из С, и это означает что массив — это просто указатель на первый элемент, и всё. То есть массив сам не знает какой он длины, и чтобы написать функцию которая обрабатывает массив, нужно 2 аргумента: указатель и длина.

Сейчас у нас есть такие типы как array и vector . Эти типы позволяют хранить либо массивы фиксированной длины (например array ) или, в случае с vector , иметь динамический массив, который расширяется и сужается по мере добавления в него элементов.

Многомерные массивы в С++ (как и в С) тоже выглядят плохо, т.к. являются всего лишь массивами указателей. Для них тоже стоит использовать array/vector , а еще лучше, если требуются вычисления, использовать специализированную библиотеку вроде Eigen.

Выделение памяти

Адресное пространство поделено на условно 2 области — стэк и куча. Стэк предназначен для временных данных, и имеет ограничения по размеру, в то время как куча — это ваша оперативная память, занимайте хоть всю.

В связи с этим, в отличии от языков со «сборкой мусора», в С++ есть два способа выделения (аллокации) памяти: на стэке и в куче.

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

Другое дело — выделение в куче. Она для более долгосрочного хранения данных, и ответственность за очистку этой памяти лежит на вас. Если забудете, будет «утечка памяти», что очень плохо.

Как видите, для выделения в куче, мы используем оператор new , и он нам возвращает указатель на выделенную память. Чтобы освободить эту память, нужно использовать оператор delete .

Значения, указатели и ссылки

Давайте начнем с опеределений. Вот есть у вас переменная int32_t x<42>; — под нее выделено 4 байта, и каждый раз когда вы «берете» x , вы получаете значение 42 (ну, пока вы его не поменяли). Запись x = 11; запишет в эту память значение 11.

У нас также есть такая штука как указатель — это переменная, которая содержит адрес другой переменной. То есть можно написать вот так:

Тип этой переменной — со звездочкой, и звездочка означает что это указатель. Ее значение — это адрес переменной x , а чтобы взять адрес чего-либо в С++ нужно посдавить амперсанд, вот так: &x .

Хитрость тут в том, что через адрес переменной можно манипулировать самой переменной. То есть вместо x = 42 можно написать *y = 42 . Заметьте что тут звездочка стоит перед именем переменной, и означает «следовать за». То есть мы идем туда, куда указывает y и в том месте памяти меняем значение.

Наконец, третий способ доступа к переменной — это ссылка. Ссылка действует так же как и указатель, только синтаксис другой. Вот смотрите:

В отличии от указателей, тип ссылки заканчивается на & , и ссылке не требуется адрес для присваивания — можно просто подсунуть ей саму переменную. Для доступа по ссылке, в отличии от указателей, тоже не нужно никакого префикса: можно просто использовать ссылку как обычную переменную.

Также, в отличии от C#/Java, в С++ ссылка не может быть пустой (null). Это значит что все ссылки должны быть инициализированы, т.е. ссылаться на что-то. Это с одной стороны хорошо (в управляемых языках многие мечтают о non-nullable objects), но с другой стороны создает много проблем — например, если в классе есть поле-ссылка, вы обязаны инициализировать ее в конструкторе.

Умные указатели

К сожалению, ни один из типов приведенных ранее не ведет себя как ссылка в C# или Java, т.к. любой объект, выделенный в куче, нужно удалять вручную. Но как это сделать если ты не знаешь, кому, когда и на сколько времени понадобится твой объект? Для этого есть умные указатели.

Умный указатель содержит в себе указатель на объект в куче и счетчик использований. Как только счетчик доходит до нуля, объект уничтожается. Выглядит это вот как-то так:

Такой объект можно передавать куда угодно и не бояться его использовать, и он — пожалуй лучший аналог ссылок в C#/Java, хотя и с ограничениями. Одно из ограничений — невозможность рекурсии, то есть нельзя писать вот так:

Поскольку нет никакого централизованного учета ссылок, подобная конструкция не приведет ни к чему хорошему — в Child вместо shared_ptr придется использовать weak_ptr ну или обычную ссылку.

shared_ptr<> также решает проблему хранения ссылок в таких коллекциях как vector . Проблема в том, что нельзя сделать vector , но вполне можно сделать vector > .

Помимо shared_ptr<> есть несколько других типов умных указателей.

Алгоритмы

С++ не обладает никаким «интерфейсом» который символизирует перечисляемые объекты. Вместо этого он использует итераторы, которые умеют ходить по той или иной структуре. Любой перечисляемый тип может определить функции begin() и end() который возвращают итераторы на первый и за-последним элементы.

С++ также определяет цикл for для обхода коллекций, то есть можно написать:

Код выше также использует ключевое слово auto (аналог var в C#) для вывода типов.

В целом, алгоритмы в С++ это глобальные функции с такими красочными названиями как count_if и всякими прелестями вроде того факта, что remove на контейнере вовсе не удаляет объекты из контейнера, а только сваливает их в конец, а собственно удаляет их функция erase() (очень неинтуитивно).

У контейнеров нет функций-членов с алгоритмами вроде sort() или search() . И добавить их в контейнер нельзя (за исключением наследования) т.к. в С++ нет функций расширения. Алгоритмы специально спроектированы так, чтобы работать на всех возможных контейнерах путем итерирования.

Заключение

Сейчас С++ становится лучше. Улучшается синтаксис языка, добавляются новые библиотеки, но стоит признать, что некоторые проблемы так и не решены, и есть множество недоработок в стандартной библиотеке, некоторые из которых кроме как переписыванием не починить.

В плане компиляторов тоже все лучше и лучше. Есть компилятор Clang (дает вменяемые ошибки при компиляции вместо километров ада), интерпретатор Cling (REPL-среда на базе Clang, рекомендую!), C++ компилятор от Microsoft поддерживает инкрементальную и Edit & Continue компиляцию, а еще скоро процесс компиляции существенно ускорится путем введения модулей.

Что касается инструментов, то ReSharper C++ (если вы пишете под Visual Studio) и CLion (кросс-платформенная IDE) позволяют комфортно писать на этом языке. А такие технологии как CUDA Toolkit и Intel Parallel Studio позволяют амортизировать популярные аппаратные платформы CUDA и Intel Xeon Phi. ■

Цукерберг рекомендует:  Библиотека для определения типа и модели устройства на PHP
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих