7 ложных предположений о том, как устроены строки

Когда речь идет о написании чего-то простого, мы, программисты, обычно действуем интуитивно. В случае с простыми вещами мы полагаемся на четкий набор предположений вместо конкретных знаний о том, как эти вещи работают. Например, мы предполагаем, что если b = a + 1 , то b больше a , или что если мы применим функцию malloc для какого-то буфера, то получим необходимое количество памяти для записи. Мы не заглядываем в документацию всякий раз, когда имеем дело с мелочами.
Мы делаем так, потому что тотальная проверка замедлит работу. Однако если бы мы все-таки провели проверку, мы бы обнаружили, что обычно ошибаемся в своих предположениях. Существует арифметическое переполнение, в результате которого a + 1 может быть значительно меньше, чем a . Иногда malloc дает нам null вместо буфера и мы оказываемся в пролете.
Нам обычно приходится обжечься на таких вещах, чтобы хотя бы немного изменить свои предположения. И даже тогда мы обычно исправляем их весьма условно.
Столкнувшись с досадной ошибкой переполнения, мы можем скорректировать свое предположение о целых числах в виде « a + 1 больше a , если отсутствует вероятность, при которой a представляет собой очень большое число». И мы действуем исходя из этого, вместо того, чтобы обдумать четкие правила, по которым работает переполнение.
Уточненные предположения – это опыт. Чаще всего они позволяют нам работать быстрее и правильнее. Однако мы можем вообще переместить некоторые вещи, например, правильную обработку malloc , из нашей внутренней категории «простые вещи» во внутреннюю категорию «сложные вещи». И тогда мы действительно можем пойти и уточнить, как они работают.
О строках
Во-первых, строки – это архетипический пример «простой вещи». Вероятнее всего, будучи детьми, мы учили буквы и цифры, и они кажутся нам очень знакомыми. Во-вторых, во время обучения программированию большинство из нас выполняло множество заданий с использованием строк, поскольку они являются практически единственным интересным встроенным типом данных в большинстве языков. Когда мы используем строки при программировании, мы вполне уверены в том, как они работают. В-третьих, мы можем иметь немало предположений насчет функционирования некоторых простых наборов символов, таких как ASCII или ISO-8859-1.
Либо потому что это мы настолько старые, либо наши учителя были настолько старыми. Это же наборы символов из тех времён, когда всё было просто!
Univac 1050-II, 1964, первый компьютер, использующий ASCII (wikipedia).
Источник: https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/UNIVAC_1050-II.jpg/1280px-UNIVAC_1050-II.jpg
Однако на самом деле строки – это очень сложная вещь. Сравните их, например, с обычным типом Int , которую вы найдете в любом языке. Мы знаем и понимаем внутреннее представление: 64 бита, дополнительный код (ну или можем потратить 15 минут и прочитать о нём в Википедии), и понимаем его семантику (ведет себя как число, кроме случаев, когда оно слишком большое или слишком маленькое). В случае со строками мы обычно знали представление (один байт на символ, см. символ в таблице ASCII), но практически никогда не знали семантику. Наша строка могла содержать имя нашего клиента. Она могла содержать число, кусок JSON или даже SQL-запрос.
Строки – это универсальный тип Any для чего угодно, и есть вероятность, что если для какого-либо элемента программы отсутствует готовое представление, то он будет храниться и обрабатываться как строка. Независимо от того, какую типизацию вы используете – динамическую или статическую – это сводит всю безопасность типов к нулю. Положение усложняется тем, что многие вещи, для которых мы используем строки, чертовски опасны, например, SQL или HTML. По этой причине SQL-инъекции и межсайтовый скриптинг год за годом возглавляют списки уязвимостей.
Но мы хотя бы понимаем, как работают строки, верно? Мы знаем, как склеивать, менять регистр и так далее, да?
Unicode
Сегодня понять, что такое строки, значительно сложнее, чем это было в 2000 г. Переход к Unicode происходит уже несколько десятилетий, и я уже несколько лет не слышал жалоб на то, что их символы отображаются неправильно. Их печать – другое дело. Надеюсь, эта проблема будет решена в 22-м веке.
Хотя Unicode прекрасен во всех остальных отношениях, он эффективно уничтожает большинство наших «полезных» предположений о том, как действительно работают строки, но об этом мало говорится. И, к сожалению, многие из нас, скорее всего, все еще работают с устаревшими предположениями об устройстве строк. И к тому же многие из нас также больше не понимают представление строк в памяти. Признаться, я и сам не понимаю, правда.
Разрушенные предположения
Давайте сейчас пройдемся по некоторым из моих устаревших предположений, которые мне пришлось отбросить вместе с набором символов ISO-8859-1. Конечно, это не исчерпывающий перечень, но, надеюсь, его будет достаточно, чтобы выкинуть (Unicode)-строки из вашего воображаемого ящичка с «простыми вещами»
Символ представляется с помощью одного байта
В старые добрые времена ASCII каждый символ занимал свои семь битов, в результате чего было легко определять размер буфера и сканировать память. В случае с Unicode это ужасное предположение. Давайте рассмотрим один условный пример, чтобы доказать это.
В некоторый момент времени разработчики WordPress боролись с внедрением кода SQL. Например, они пытались исправить проблему с добавлением нежелательных одинарных кавычек в вводимые пользователем данные и последующее повреждение базы данных. Что-то вроде такого выдуманного примера:
↓↓ (Пользователь использует «whocares’ or true — » в качестве пароля)
Итак, самый простой способ решения этой проблемы, какой можно представить, – это правильно закодировать одинарную кавычку во входных данных. (Но это просто только в воображении. Не делайте так.) То есть каждая одинарная кавычка ‘ должна быть закодирована как \’ или одинарная кавычка с обратной косой.
Тогда PHP-разработчики написали функцию addslashes , и какое-то время все было хорошо. Единственной проблемой было то, что они предусмотрели экранирование байт за байтом, а не символ за символом. Разработчики не заметили эту проблему еще и потому, что они работали только с однобайтовыми символами Unicode (по большей части прежним ASCII). Затем кто-то сообразил, что если вы внесете в систему такую строку, как «뼧 or true — » , то снова получите SQL-инъекцию.
Чтобы понять, почему это происходит, давайте посмотрим, как эти символы представлены в Unicode:
| код | символ |
|---|---|
| 0xbf27 | 뼧 |
| 0xbf5c | 뽜 |
| 0x27 | ‘ |
| 0x5c | \ |
Фактически функция addslashes заменяла все значения байтов 27 на байты 5c 27 . Таким образом, «뼧 or true — » превратилось в «뽜’ or true — » , после чего снова появились инъекции.
Нетрудно представить другие подобные катастрофы.
Длина строк – это нечто устойчивое
В ASCII многочисленные простые операции обработки строк не влияли на длину строк. В Unicode это не так. Возможно, это свойство имеет значение только если вы вручную распределяете буферы и пытаетесь определить размер графики, но давайте рассмотрим несколько случаев, когда длина строк неожиданно меняется.
Во-первых, в качестве примера обычной операции над строкой – выполняется ли length(x) = length(toUpper(x)) для x в Unicode? Нет, поскольку в Unicode, помимо прочего, есть символы лигатуры, такие как fi , которые увеличиваются вдвое до FI .
Второй пример относится к нормализации. Поскольку для одного символа существует множество кодовых точек, Unicode заставляет вас производить нормализацию, чтобы, например, не оказалось двух пользователей с одинаковыми именами. Можно предположить, что нормализация или процесс выбора канонического представления для некоторого набора символов не повлияет на число нормализованных символов, однако это происходит: единый символ ﷺ увеличивается в 18 раз до صلى الله عليه وسلم .
Таким образом, возможно, не стоит делать никаких предположений насчет длины строк после какой-либо операции.
Верхний и нижний регистры каким-то образом связаны
Нам, жившим с вариантами ASCII, свойственно часто использовать операции с верхним и нижним регистром. Помимо того, что теперь они могут менять длину строк, существуют и другие опасности. Важнее всего то, что прежнее предположение об уникальной связи букв верхнего и нижнего регистра ушло.
В Unicode в результате конвертации строки в верхний регистр можно потерять больше, чем просто информацию о том, в каком регистре были символы. Например, если вы переведете в нижний регистр символ кельвина K , вы получите обычный символ k в нижнем регистре, без возможности обратного перевода. Это имеет на удивление большое значение при выполнении нечувствительных к регистру сравнений, поскольку toLower(‘K’) == toLower(‘k’) , но toUpper(‘K’) != toUpper(‘k’) .
Почему они называются буквами верхнего и нижнего регистра: см. происхождение термина ‘верхний регистр’. (wikipedia)
Источник: https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Upper_case_and_lower_case_types.jpg/800px-Upper_case_and_lower_case_types.jpg
Пробел – это 0x20
Это предположение по-прежнему верно. Байт 0x20 представляет пробел в Unicode. Однако то же самое делают U+2000, U+2001, U+2002 и многие другие, в том числе знак пробела нулевой ширины U+FEFF. Пробельный символ (whitespace) является особенным. Мы не можем использовать такие имена, как «TheAlex» и «TheAlex » одновременно, так как HTML не покажет такой пробел, и остальные пользователи не увидят разницы. Поэтому перед обработкой мы должны удалить пробел в начале и в конце.
И вот здесь Unicode дает возможность здорово пролететь. Всего-то надо в одном месте кода забыть о разнообразии пробелов и мы получим ненормализованные данные в своей базе данных. И тогда местами возникают проблемы.
Символы выглядят по-разному
В отличие от ASCII Unicode содержит множество кодовых значений для одного и того же символа и множество символов, которые выглядят почти или абсолютно одинаково, но не являются одним и тем же символом. В качестве конкретного примера вставьте «tyрeablе» == «typeable» в ваш любимый REPL. Пригодится repl.it, если у вас ничего нет под рукой.
Получили False ? Это из-за того, что «р» здесь – не латинское «р», а русская буква.
Чтобы пояснить, почему это является проблемой, давайте используем этот кусок нашей схемы базы данных в качестве примера:
Могу предположить, что в эпоху Unicode эти ограничения вообще не имеют смысла. При вводе пользовательской информации пользователь может свободно подделать любой адрес или имя, какое ему угодно. Это позволяет пользователю попытаться осуществить любую кражу, используя, скажем, такое же имя, как у какого-нибудь другого пользователя. Кроме того, такие вещи, как адреса, не остаются только цифровыми. Рано или поздно адрес прочитают или напечатают, и тогда различие, видимое для базы данных, исчезнет. Существует ли в вашем процессе что-либо аналоговое, что можно использовать, притворившись другим пользователем?
Конечно, эта проблема появилась раньше, чем Unicode, особенно в некоторых наборах символов, таких как ISO-8859-5. Однако Unicode усугубляет ее серьезность и масштаб. Получается, вы не можете делать практически никаких предположений о том, как строка будет выглядеть.
Текст пишется слева направо
И что будет, если я скопирую это в свой терминал?
rm -rf your_home_directory # dlrow olleh ohce
Попробуйте сами. Если вы беспокоитесь о домашнем каталоге, вы можете использовать какое-нибудь простое поле ввода текста вместо своего терминала.
В некоторых языках текст не пишется слева направо, и чтобы учесть это, Unicode использует эти коды для ‘смены направления письма’. Фактически текст тот же, несмотря на то, что он пишется справа налево, поэтому ваш терминал может попробовать стереть файлы, если бы вы попробовали использовать мой пример.
В урду текст пишется справа налево.
Помимо розыгрышей коллег в Teams, это двухстороннее письмо часто используется для фальсификаций, самой популярной из которых уже давно является запись длинных URL наоборот, чтобы они выглядели безвредными.
Строки декодируются одинаково
Одно из предположений насчет ASCII (и его вариантов) заключалось в том, что декодирование является тривиальной задачей, ошибки в которой маловероятны. Некоторые из моих коллег по университету бегло читают ASCII из шестнадцатеричного дампа! Это значило, что единственная проблема при передаче данных в виде строк заключалась в правильном парсинге их содержимого.
Unicode, как многобайтовая кодировка, добавляет еще один шаг. Сначала нужно пропарсить строки, а затем приступать к контенту.
Нужно сказать, что парсинг – это трудная область, известная способностью вызывать проблемы безопасности. Одна из основных проблем состоит в том, что одна и та же строка может парситься по-разному в разных программах. Хороший современный пример – когда санитайзер html (штука, которая останавливает XSS) говорит на диалекте HTML, слегка отличающемся от того, на котором говорит браузер пользователя. Если эти элементы по-разному интерпретируют какую-либо строку, санитайзер может решить, что в ней нет скриптов и другого вредоносного кода, в то время как браузер будет иметь несколько иную интерпретацию и начнет исполнять элементы входных данных как скрипты. Использование одного и того же канала для команд и контента равносильно добавлению null в языки программирования – а это ошибка на миллиард долларов!
Unicode усугубляет эту проблему, так как не все Unicode-парсеры одинаково трактуют все совокупности байтов. В основном, по-разному обрабатываются недопустимые Unicode-последовательности. Например, «e3 80 22″ – это неправильная Unicode-последовательность, и один Unicode-парсер может расценить ее как недопустимый символ, тогда как другой парсер может быть менее строгим и интерпретировать ее как три символа: ã , \x80 и » . Таким образом, в веб контексте последний символ может стать проблемой, поскольку он может позволить XSS пройти через значения атрибутов.
Мысли в заключение
Для меня, как для инженера-программиста, Unicode создает много сложностей, большинство из которых мне не нужны. Перечисленные выше подводные камни можно обойти по отдельности без особого труда, но их присутствие может оказать существенное влияние на систему в целом. Таким образом, вы должны решить, какие виды строк вы допускаете в своей системе, придумать, как правильно их нормализовать, как устранить омоглифы и убрать начальные и конечные пробелы.
Проблема здесь в том, что все такие шаги должны происходить единообразно. Если вы нормализуете строку определенным образом в одной части своей программы, а в другой части это происходит по-другому, вы получаете противоречие или – в худшем случае – проблему безопасности. Вы также должны это учитывать, потому что, увы, ошибки случаются, и стараться точно записывать все, что вы делали с каждой строкой, чтобы можно было это учесть при их использовании.
И, к сожалению, вы не можете просто ‘починить строки’ в каждой точке использования. Некоторые операции над строками можно безопасно выполнить только один раз, иначе вас ждет потеря информации или что-нибудь похуже. Вы должны знать и отслеживать семантику строк, чтобы понимать, какие шаги нужно сделать и какие шаги вы не можете сделать в вашем рабочем контексте.
Дополнение: писатель из меня так себе, поэтому я чувствую, что стоит еще раз подчеркнуть исходную мысль, а то вы подумаете, что я какой-то фанат ASCII.
Некоторые считают, что здесь приводятся доводы против Unicode, но это не так. Я не хочу возвращаться к ISO-8859-1, потому что это отстой. Я также готов мириться с большими сложностями, которые позволяют людям правильно записывать свои имена. Здесь я только пытаюсь доказать, что работа c Unicode неизбежно связана с бо́льшими трудностями, чем работа с ASCII. И при этом я вижу, что в отношении обработки строк люди имеют кучу убеждений со времен ASCII, не работающих с Unicode.
Некоторые из приведенных примеров относятся к низкому уровню, некоторые уже устарели, но некоторые, такие как омоглифы, встречаются повсеместно. Актуальны они для вас или нет, зависит от вида работы, которую вы выполняете, и от языка, который вы используете.
(Кроме того, в первом примере представьте, что UTF-1 и PHP не имеют строк с завершающим нулем)
Что такое поисковая строка

Такие строки встречаются на сайтах, где много информации. Пользователь может воспользоваться такой строкой и найти нужную информацию на сайте.
Но самый яркий пример — это поисковые системы.
Как выглядит поисковая строка
В поисковых системах
Вот так выглядит поисковая строка Яндекса.

После ввода запроса следует нажать кнопку “Найти”, и наслаждаться результатом отбора. После ввода запросы можно уточнить результаты, выбрав кнопку расширенного поиска.

Расширенный поиск позволит отобрать наиболее релевантные результаты.

Поисковик предусматривает работу с различными фильтрами (поиск по времени, картинкам, новостям и т.д.). С помощью них вы можете уточнить запрос: ввести ограничение по региону, словоформе, определенному интернет-сайту, типу файла, языку, дате обновления веб-страницы и т.д. Активные фильтры изменяют цвет, поисковик при этом дает выдачу автоматически, согласно заданным условиям.
Под строкой поиска есть дополнительные быстрые фильтры. Вы можете искать только картинки, информацию на картах и т. д.

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

В Google можно также работать с системой быстрой фильтрации поиска (горизонтальное меню под строкой).

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

Интересные особенности поисковых строк в каждой ПС
При введении ключевого запроса каждый поисковик (Яндекс, Google и другие) выдает родственные вашему введенному запросу ключи (которые чаще всего искали люди, а также вводил ранее пользователь вашего компьютера).
Например, вы вводите ключевик “Воронеж”. ПС выдает всплывающее окно с подсказками, при выборе одной из них вы можете уточнить свой запрос. К примеру, он может выдать дополнительные ключи типа “Воронеж на карте России”, “Воронежская область”, “Воронеж прощание с Романом Филипповым”. Понятно, что запрос “Воронеж” слишком объемный, и поисковику до конца не ясно, что именно ищет пользователь. При уточнении ключа вы получите более релевантную выдачу.

Зачем нужны подсказки в поисковиках
Главная задача, заложенная разработчиками — помощь пользователю в его поиске. Со временем SEO-специалисты стали ориентироваться на подсказки при продвижении сайта, в частности, для составления семантического ядра (подбора ключей, по которым происходит продвижение сайта). Как правило, это низкочастотники или среднечастотники, с синонимами и хвостами, информацию по которым ищут пользователи Интернета.
Как отключить подсказки в поисковиках
Иногда всплывающее окно с подсказками мешает при работе с поисковиком: например, ПК пользуется не один человек, и он хочет сохранить конфиденциальность поиска. Или старые запросы пользователя, которые выдаются во всплывающем окне, мешают при поиске. Рассмотрим, как удалить подсказки по индивидуальному поиску на примере ПС Яндекс. В правом верхнем углу главной страницы нажимаем “Настройка”, затем “Настройка портала” и “Результаты”. Следует убрать маркеры в разделе “Персональный поиск” и сохранить конфигурацию.

Чтобы удалить некоторые подсказки или настроить их порядок выдачи по-другому — при вводе запроса перейдите на окно подсказок. Напротив каждого ключа есть кнопка удаления, а изменить порядок можно путем простого перетаскивания выбранного ключа вверх по окну или вниз.
Поиск через браузеры
Современные браузеры позволяют вводить запрос прямо в адресной строке.
В каждом браузере по умолчанию после ввода запроса открывается определенная ПС, изменить которую можно в его настройках.
Google Сhrome

Запрос можно ввести в одно из двух полей. Верхнее поле (это и есть адресная строка) в нашем примере автоматически подключит поисковую строку Google. Нижнее служит для удобства и расширяет функционал браузера. В нашем примере оно подключает поиск Mail. Работает полноценное окно подсказок, о которых мы расскажем ниже.
Mozilla Firefox

При вводе ключа реализован поиск посредством ПС Яндекс. Причем, всплывают только подсказки по вашему предыдущему поиску. Обращения к подсказкам ПС не происходит.
Opera

По умолчанию помогает в поиске зарубежный Google, помогая подсказками. Для удобства в самом интерфейсе веб-браузера установлены ссылки на несколько поисковых систем. Кроме того, искать что-то можно через саму адресную строку вверху.
Как изменить поисковую систему в адресной строке
Допустим, вам не подходит Яндекс, нужен Google. Как мы уже говорили ранее, поменять вызов предустановленного поисковика через адресную строку можно в настройках каждого браузера. Рассмотрим, как изменить основной поисковик в браузере Google Chrome.
Выберите символ в правом верхнем углу открытого браузера.

Перейдите в настройки. В данном разделе можно изменить поисковую систему для адресной строки и интерфейса.

Подсказки в поисковой строке веб-браузера
Браузер, также как и ПС, предлагает помощь пользователю в виде подсказок при вводе запроса. При вводе еще самых первых букв браузер уже выдает соответствующие запросы из введенных вами ранее. Кроме запросов он выдает и названия/адреса посещенных страниц, которые содержат в себе вводимое сочетание букв.

Как очистить поисковую строку в браузере от подсказок
Каждый введенный вами (и не только вами) запрос через браузер хранится в его кэше. Список похожих запросов выпадает постоянно, если вы вводите новый похожий ключ в браузере. Для очистки кеша следует принудительно их удалить или установить запрет на кеширование.
IE
В «Сервис» раздел «Свойства» найдите историю просмотров, промаркируйте все пункты, указывающие на ключевые запросы, сохраните изменения. В «Поиск» найдите предустановленный поисковик и для запрета кеширования выберите «Отключить».
Opera
Нажмите Ctrl и одновременно F12. Активируйте режим «Поиск». Отключите подсказки. Затем войдите в «Расширенные»-«История» и удалите кэш диска.
Mozilla Firefox
Пройдите в «Настройки», и включите «Приватность». Там же удалите историю и установите сроки ее очищения в будущем. Отметьте маркеры на пунктах «Кэш» и «Журнал форм и поиска», очистите их.
Рассмотрим подробнее, как удалять поисковые подсказки на примере Google Chrome.
Для перехода к настройкам нажмите на символ “три точки” вверху справа. Выберите “Настройки” — “Дополнительные”.

Уберите указанный маркер.

Также в каждом браузере можно удалить какую-либо конкретную подсказку. К примеру, в Хроме надо ввести запрос, выбрать ненужную подсказку, нажать Shift + Delete.
Строки в C#: введение в работу со строками
Большое количество задач (если не большинство) при разработке программного обеспечения так или иначе связано с обработкой строк будь то простой вывод в консоль определенных значений, сравнение строк или парсинг текста web-страниц. Так или иначе, даже при первом знакомстве с языком программирования, первое, что мы делаем — это используем строки («Hello world» и т.д.). Именно поэтому умение работы со строками в C#, как и в любом другом языке программирования, является одним из необходимых и важных навыков программиста. В этой и нескольких последующих статьях мы рассмотрим основные возможности работы со строками в C#.
Строка в C#
Строки в C# относятся к неизменяемым типам данных и представляют собой объекты класса System.String . Объекты этого класса представляют текст в виде последовательности символов Unicode. То есть, в отличие от Delphi, где есть и UnicodeString и AnsiString и т.д. и т.п, в C# строка — это всегда набор символов Unicode. Однако, при необходимости, никто не мешает нам изменять кодировку символов в строке C# и об этом мы тоже поговорим.
Также, для тех, кто уже имел дело с другими языками программирования, не лишним будет знать, что в конце строки C# нет нуль-символов. Поэтому строка в C# мы можем создавать строки, которые содержат любое количество внедренных нуль-символов ( \0 ). При этом, максимальный размер объекта String в памяти составляет 2 Гб.
Создание строк в C#
Объявление и создание строк в C# можно осуществить несколькими способами. Рассмотрим основные из них.
В любом из представленных выше примеров мы создаем строку, при этом, в последнем случае, мы создаем пустую строку без символов. Так как строки относятся к ссылочным типам данных, то никто нам не запрещает присвоить строке значение null
хотя, разработчики C# и предупреждают нас о том, что использование string.Empty вместо null предпочтительнее и снижает вероятность получения исключения типа NullReferenceException .
Строки с регулярными и буквальными литералами
Отдельное внимание при создании строк также стоит уделить такому моменту, как создание строк, содержащих какие-либо служебные символы. Например, если вам необходимо присвоить строке значение, указывающее на путь к файлу: c:\Program Files\dotnet\dotnet.exe . Если вы попробуете присвоить строке это значение как есть, то ваша программа даже не будет скомпилирована, так как Visual Studio укажет вам на ошибку:
Все дело в том, что символ обратной косой черты \ используется в строках для указания так называемых escape-последовательностей и для того, чтобы создать строку, содержащую символ \ нам необходимо либо регулярный строковый литерал , либо буквальный литерал , как показано ниже:
Использование регулярного литерала позволяет нам использовать различные escape-последовательности в строках. В таблице ниже представлены основные escape-последовательности в C#:
| Escape-последовательность | Имя символа | Кодировка Юникод |
| \’ | Одинарная кавычка | 0x0027 |
| \» | Двойная кавычка | 0x0022 |
| \\ | Обратная косая черта | 0x005C |
| \0 | Null | 0x0000 |
| \a | Предупреждение | 0x0007 |
| \b | Backspace | 0x0008 |
| \f | Перевод страницы | 0x000C |
| \n | Новая строка | 0x000A |
| \r | Возврат каретки | 0x000D |
| \t | Горизонтальная табуляция | 0x0009 |
| \v | Вертикальная табуляция | 0x000B |
| \u | Escape-последовательность Юникода (UTF-16) | \uHHHH (диапазон: 0000–FFFF; пример: \u00E7 = «ç») |
| \U | Escape-последовательность Юникода (UTF-32) | \U00HHHHHH (диапазон: 000000–10FFFF; пример: \U0001F47D = «») |
| \x | Escape-последовательность Юникода аналогична «\u», она отличается только длиной переменной | \xH[H][H][H] (диапазон: 0–FFFF; пример: \x00E7 или \x0E7 или \xE7 = «ç») |
Например, используя escape-последовательности, мы можем вывести в консоль таблицу:
В итоге, в консоли мы увидим вот такую красивую табличку:

Что касается использования буквального литерала в строках, то его удобно использовать для того, чтобы сделать строки, содержащие какие-либо служебные символы, более читабельными. Согласитесь, что такая строка:
выглядит более читабельной, чем вот такая:
Более того, так как буквальный литерал позволяет сохранять все символы в строке как есть, то, используя его, мы можем использовать в наших строках переносы, кавычки и прочие символы как есть, например:
В результате, в консоли мы увидим следующий текст:
Неизменяемость строк в C#
Как уже упоминалось выше, строки в C# относятся к неизменяемым типам данных. Что это значит для нас? А это значит, что при каждом присвоении значения переменной типа string система вначале освобождает память, занятую строкой, а затем выделяет по новой и только потом записывает новое значение. С одной стороны, подобный подход выглядит нерациональным — лишние освобождения и выделения памяти, но, с другой стороны, таким образом разработчики C# обеспечили максимальную безопасность работы со строками и, надо сказать, сделали это достаточно элегантно и понятно. Например,
Также стоит отметить, что к огда содержимое двух строк, например, s1 и s2 объединяется для формирования строки, то две исходные строки не изменяются. Например:
В примере выше оператор += создает новую строку, которая содержит объединенное содержимое двух строк. Этот новый объект присваивается переменной s1 , а исходный объект, который был присвоен s1 , освобождается для сборки мусора, так как ни одна переменная не ссылается на него.
Если вы создадите ссылку на строку, а затем «измените» исходную строку, ссылка будет по-прежнему указывать на исходный объект, а не на новый объект, который был создан при изменении строки. Пример:
В представленном примере может показаться, что в итоге, в консоли будет строка «Hello world» , однако, на самом деле в консоли мы увидим только «Hello » так как строка s2 остается неизменной.
Итого
Сегодня мы узнали как создавать строки в C#, как использовать различные escape-последовательности в строках, а также использовать буквальный литерал для повышения читабельности строк в C#. Также рассмотрели некоторые моменты, касающиеся неизменности строк в C# и как эта неизменность проявляется.