Строковый литерал
Строковый литерал или анонимная строка — это литерал строкового значения в исходном коде компьютерной программы. В современных языках программирования обычно используется последовательность символов в кавычках, формально «разделители в скобках», например , где — строковый литерал со значением. Такие методы, как escape-последовательности, можно использовать, чтобы избежать проблемы столкновения разделителей (проблемы с скобками) и позволить встроить разделители в строку. Существует множество альтернативных обозначений для указания строковых литералов, особенно в сложных случаях. Точное обозначение зависит от рассматриваемого языка программирования. Тем не менее, существуют общие рекомендации, которым следуют большинство современных языков программирования.
Синтаксис
[ редактировать ]Разделители в квадратных скобках
[ редактировать ]В большинстве современных языков программирования используются разделители в скобках (также сбалансированные разделители ). для указания строковых литералов. Двойные кавычки являются наиболее распространенными разделителями кавычек:
"Hi There!"
буквально Пустая строка записывается парой кавычек без каких-либо символов между ними:
""
Некоторые языки либо допускают, либо требуют использования одинарных кавычек вместо двойных (строка должна начинаться и заканчиваться кавычками одного и того же типа, а тип кавычек может давать или не давать немного разную семантику):
'Hi There!'
Эти кавычки непарные (один и тот же символ используется в качестве открывающего и закрывающего), что является пережитком технологии пишущих машинок , которая была предшественником самых ранних компьютерных устройств ввода и вывода.
В терминах регулярных выражений базовый строковый литерал в кавычках задается как:
"[^"]*"
Это означает, что строковый литерал записывается как: кавычка, за которой следует ноль, один или несколько символов, не являющихся кавычками, а затем кавычка . На практике это часто усложняется экранированием, другими разделителями и исключением символов новой строки.
Парные разделители
[ редактировать ]В ряде языков предусмотрены парные разделители, в которых открывающий и закрывающий разделители различны. Они также часто допускают вложенные строки, поэтому разделители могут быть встроены, если они парные, но все равно приводят к коллизии разделителей при внедрении непарного закрывающего разделителя. Примеры включают PostScript , в котором используются круглые скобки, как в (The quick (brown fox))
и m4 , который использует обратную галочку (`) в качестве начального разделителя и апостроф (') в качестве конечного разделителя. Tcl допускает как кавычки (для интерполированных строк), так и фигурные скобки (для необработанных строк), как в "The quick brown fox"
или {The quick {brown fox}}
; это происходит из-за одинарных кавычек в оболочках Unix и использования фигурных скобок в C для составных операторов, поскольку блоки кода в Tcl синтаксически то же самое, что и строковые литералы - чтобы сделать это возможным, важно, чтобы разделители были парными.
Набор символов Юникода включает парные (отдельные открывающие и закрывающие) версии как одинарных, так и двойных кавычек:
“Hi There!” ‘Hi There!’ „Hi There!“ «Hi There!»
Однако они используются редко, поскольку многие языки программирования их не регистрируют (единственным исключением являются парные двойные кавычки, которые можно использовать в Visual Basic .NET ). Непарные знаки предпочтительнее из соображений совместимости, поскольку их легче печатать на самых разных клавиатурах, и поэтому даже на языках, где они разрешены, многие проекты запрещают их использование в исходном коде.
Разделители пробелов
[ редактировать ]Строковые литералы могут заканчиваться символами новой строки.
Одним из примеров являются параметры шаблона MediaWiki .
{{Navbox |name=Nulls |title=[[wikt:Null|Nulls]] in [[computing]] }}
Для многострочных строк может быть специальный синтаксис.
В YAML строковые литералы могут указываться относительным расположением пробелов и отступ.
- title: An example multi-line string in YAML
body : |
This is a multi-line string.
"special" metacharacters may
appear here. The extent of this string is
represented by indentation.
Без разделителей
[ редактировать ]Некоторые языки программирования, такие как Perl и PHP, в некоторых контекстах допускают строковые литералы без каких-либо разделителей. Например, в следующей программе Perl: red
, green
, и blue
являются строковыми литералами, но не заключаются в кавычки:
%map = (red => 0x00f, blue => 0x0f0, green => 0xf00);
В большинстве контекстов Perl рассматривает незарезервированные последовательности буквенно-цифровых символов как строковые литералы. Например, следующие две строки Perl эквивалентны:
$y = "x";
$y = x;
Декларативная нотация
[ редактировать ]В исходном языке программирования FORTRAN (например) строковые литералы записывались в так называемой Холлерита нотации , где за десятичным числом символов следовала буква H, а затем символы строки:
35HAn example Hollerith string literal
Этот декларативный стиль записи контрастирует с кавычками-разделителями в квадратных скобках , поскольку он не требовать использования сбалансированных символов в квадратных скобках по обе стороны строки.
Преимущества:
- исключает поиск текста (для символа-разделителя) и, следовательно, требует значительно меньше накладных расходов.
- позволяет избежать проблемы столкновения разделителей
- позволяет включать метасимволы , которые в противном случае можно было бы принять за команды
- может использоваться для весьма эффективного сжатия данных в виде простых текстовых строк. [ нужна ссылка ]
Недостатки:
- этот тип обозначения подвержен ошибкам, если программисты используют его для ручного ввода.
- особая осторожность необходима в случае многобайтовых кодировок
Однако это не является недостатком, когда префикс генерируется алгоритмом, что наиболее вероятно. [ нужна ссылка ]
Функции конструктора
[ редактировать ]В C++ есть два стиля строк: один унаследован от C (разделен "
), и тем безопаснее std::string
в стандартной библиотеке C++. std::string
class часто используется так же, как строковый литерал в других языках, и его часто предпочитают строкам в стиле C из-за его большей гибкости и безопасности. Но это приводит к снижению производительности для строковых литералов, поскольку std::string
обычно выделяет память динамически и должен копировать в нее строковый литерал в стиле C во время выполнения.
До C++11 не существовало литерала для строк C++ (C++11 позволяет "this is a C++ string"s
с s
в конце литерала), поэтому использовался обычный синтаксис конструктора, например:
std::string str = "initializer syntax";
std::string str("converting constructor syntax");
std::string str = string("explicit constructor syntax");
все они имеют одну и ту же интерпретацию. Начиная с C++11, появился новый синтаксис конструктора:
std::string str{"uniform initializer syntax"};
auto str = "constexpr literal syntax"s;
Столкновение разделителей
[ редактировать ]Если при использовании кавычек кто-то хочет представить сам разделитель в виде строкового литерала, он сталкивается с проблемой коллизии разделителей . Например, если разделителем является двойная кавычка, нельзя просто представить саму двойную кавычку с помощью литерала """
поскольку вторая кавычка интерпретируется как конец строкового литерала, а не как значение строки, и аналогично нельзя написать "This is "in quotes", but invalid."
поскольку средняя часть кавычек вместо этого интерпретируется как вне кавычек. Существуют различные решения, наиболее универсальным из которых является использование escape-последовательностей, таких как "\""
или "This is \"in quotes\" and properly escaped."
, но есть много других решений.
Парные кавычки, такие как фигурные скобки в Tcl, допускают вложенные строки, например {foo {bar} zork}
но не решайте иначе проблему коллизии разделителей, так как несбалансированный закрывающий разделитель нельзя просто включить, как в {}}
.
Удвоение
[ редактировать ]В ряде языков, включая Pascal , BASIC , DCL , Smalltalk , SQL , J и Fortran , конфликты разделителей избегаются за счет удвоения кавычек, которые должны быть частью строкового литерала. сам:
'This Pascal string''contains two apostrophes'''
"I said, ""Can you hear me?"""
Двойное цитирование
[ редактировать ]Некоторые языки, такие как Fortran , Modula-2 , JavaScript , Python и PHP, допускают использование более одного разделителя кавычек; в случае двух возможных разделителей это называется двойными кавычками . Обычно это означает, что программисту разрешено использовать как одинарные, так и двойные кавычки взаимозаменяемо — каждый литерал должен использовать то или другое.
"This is John's apple."
'I said, "Can you hear me?"'
Однако это не позволяет иметь один литерал с обоими разделителями. Эту проблему можно обойти, используя несколько литералов и конкатенацию строк :
'I said, "This is ' + "John's" + ' apple."'
В Python есть конкатенация строковых литералов , поэтому последовательные строковые литералы объединяются даже без оператора, поэтому это можно сократить до:
'I said, "This is '"John's"' apple."'
Цитирование разделителя
[ редактировать ]В C++11 появились так называемые необработанные строковые литералы . Они состоят, по существу, из
R"
идентификатор конца строки(
содержание)
идентификатор конца строки"
,
то есть после R"
программист может ввести до 16 символов, за исключением пробелов, круглых скобок и обратной косой черты, которые образуют идентификатор конца строки (его цель состоит в том, чтобы повториться для обозначения конца строки, для краткости eos id ), а затем Требуется открывающая скобка (для обозначения конца идентификатора eos). Затем следует фактическое содержимое литерала: можно использовать любые символы последовательности (за исключением того, что они не могут содержать закрывающую скобку, за которой следует идентификатор eos после кавычки), и, наконец, для завершения строки — закрывающую скобку, идентификатор eos. , и требуется цитата.
Самый простой случай такого литерала — с пустым содержимым и пустым идентификатором eos: R"()"
.
Идентификатор eos сам может содержать кавычки: R""(I asked, "Can you hear me?")""
является допустимым литералом (идентификатор eos равен "
здесь.)
Escape-последовательности не работают с необработанными строковыми литералами.
D поддерживает несколько разделителей в кавычках, причем такие строки начинаются с q"
плюс открывающий разделитель и заканчивается соответствующим закрывающим разделителем и "
. Доступные пары разделителей: ()
, <>
, {}
, и []
; непарный разделитель, не являющийся идентификатором, является собственным закрывающим разделителем. Парные разделители вложены друг в друга, так что q"(A pair "()" of parens in quotes)"
является допустимым литералом; пример с невложенностью /
персонаж q"/I asked, "Can you hear me?"/"
.
Подобно C++11, D допускает использование литералов в стиле «здесь-документ» с идентификаторами конца строки:
q"
-string-id новой содержимое строки end- of"
В D идентификатор конца строки должен быть идентификатором (буквенно-цифровыми символами).
В некоторых языках программирования, таких как sh и Perl , существуют разные разделители, которые обрабатываются по-разному, например, выполняется ли интерполяция строк или нет, и поэтому необходимо соблюдать осторожность при выборе того, какой разделитель использовать; см. различные типы строк ниже.
Множественное цитирование
[ редактировать ]Дальнейшим расширением является использование множественных кавычек , которое позволяет автору выбирать, какие символы должны указывать границы строкового литерала.
Например, в Perl :
qq^I said, "Can you hear me?"^
qq@I said, "Can you hear me?"@
qq§I said, "Can you hear me?"§
все дают желаемый результат. Хотя эта нотация более гибкая, ее поддерживают немногие языки; Помимо Perl, их также поддерживают Ruby (под влиянием Perl) и C++11 . Вариантом множественных кавычек является использование строк в стиле документа .
Lua (начиная с версии 5.1) предоставляет ограниченную форму множественного цитирования, в частности, для возможности вложения длинных комментариев или встроенных строк. Обычно используется [[
и ]]
для разделения литеральных строк (начальная новая строка удалена, в противном случае - необработанная), но открывающие скобки могут включать любое количество знаков равенства, и только закрывающие скобки с одинаковым количеством знаков закрывают строку. Например:
local ls = [=[
This notation can be used for Windows paths:
local path = [[C:\Windows\Fonts]]
]=]
Множественные кавычки особенно полезны с регулярными выражениями , которые содержат обычные разделители, такие как кавычки, поскольку это позволяет избежать необходимости их экранирования. Ранний пример — sed , где в команде подстановки s/regex/replacement/
косая черта по умолчанию /
разделители могут быть заменены другими символами, как в s,regex,replacement,
.
Функции конструктора
[ редактировать ]Другой вариант, который редко используется в современных языках, — использовать функцию для создания строки, а не представлять ее через литерал. Обычно это не используется в современных языках, поскольку вычисления выполняются во время выполнения, а не во время анализа.
Например, ранние версии BASIC не включали escape-последовательности или какие-либо другие обходные пути, перечисленные здесь, и поэтому вместо этого приходилось использовать CHR$
функция, которая возвращает строку, содержащую символ, соответствующий ее аргументу. В ASCII кавычка имеет значение 34, поэтому для представления строки с кавычками в системе ASCII следует написать
"I said, " + CHR$(34) + "Can you hear me?" + CHR$(34)
В C аналогичная возможность доступна через sprintf
и %c
Спецификатор формата «символ», хотя при наличии других обходных путей он обычно не используется:
char buffer[32];
snprintf(buffer, sizeof buffer, "This is %cin quotes.%c", 34, 34);
Эти функции-конструкторы также можно использовать для представления непечатаемых символов, хотя вместо них обычно используются escape-последовательности. Похожий метод можно использовать в C++ с помощью std::string
оператор стрингификации.
Escape-последовательности
[ редактировать ]Escape-последовательности — это общий метод представления символов, которые в противном случае трудно представить напрямую, включая разделители, непечатаемые символы (например, символы возврата), символы новой строки и пробелы (которые иначе невозможно различить визуально), и имеют долгую историю. Соответственно, они широко используются в строковых литералах, а добавление escape-последовательности (к одному символу или по всей строке) называется экранированием .
Один символ выбирается в качестве префикса, чтобы дать кодировку для символов, которые трудно или невозможно включить напрямую. Чаще всего это обратная косая черта ; Помимо других символов, ключевым моментом является то, что сама обратная косая черта может быть закодирована как двойная обратная косая черта. \\
а для строк с разделителями сам разделитель может быть закодирован путем экранирования, скажем, с помощью \"
for ". Регулярное выражение для таких экранированных строк можно задать следующим образом, как указано в спецификации ANSI C : [1] [а]
"(\\.|[^\\"])*"
означает «кавычку; за которой следует ноль или более экранированных символов (обратная косая черта, за которой следует что-то, возможно, обратная косая черта или кавычка), или неэкранирующийся символ, не являющийся кавычкой; заканчивающийся кавычкой» – единственная проблема заключается в различении завершающая цитата из цитаты, которой предшествует обратная косая черта, которую можно экранировать. За обратной косой чертой могут следовать несколько символов, например: \uFFFF
, в зависимости от схемы экранирования.
Затем экранированную строку необходимо лексически проанализировать , преобразуя экранированную строку в неэкранированную строку, которую она представляет. Это делается на этапе оценки общего лексирования компьютерного языка: оценщик лексера общего языка выполняет свой собственный лексер для экранированных строковых литералов.
Помимо прочего, должна быть возможность закодировать символ, который обычно завершает строковую константу, плюс должен быть какой-то способ указать сам escape-символ. Escape-последовательности не всегда красивы и просты в использовании, поэтому многие компиляторы предлагают и другие способы решения распространенных проблем. Однако escape-последовательности решают любую проблему с разделителями, и большинство компиляторов интерпретируют escape-последовательности. Когда escape-символ находится внутри строкового литерала, это означает «это начало escape-последовательности». Каждая escape-последовательность определяет один символ, который должен быть помещен непосредственно в строку. Фактическое количество символов, требуемых в escape-последовательности, варьируется. Escape-символ находится в верхней/левой части клавиатуры, но редактор его переведет, поэтому его невозможно записать в строку напрямую. Обратная косая черта используется для представления escape-символа в строковом литерале.
Многие языки поддерживают использование метасимволов внутри строковых литералов. Метасимволы интерпретируются по-разному в зависимости от контекста и языка, но обычно представляют собой своего рода «команду обработки» для представления печатаемых или непечатаемых символов.
Например, в строковом литерале C , если за обратной косой чертой следует такая буква, как «b», «n» или «t», то это представляет собой непечатаемый символ возврата , новой строки или символа табуляции соответственно. Или, если за обратной косой чертой следуют 1–3 восьмеричные цифры, то эта последовательность интерпретируется как представление произвольной кодовой единицы с указанным значением в кодировке литерала (например, соответствующий код ASCII для литерала ASCII). Позже это было расширено, чтобы обеспечить более современную запись шестнадцатеричного кода символов:
"I said,\t\t\x22Can you hear me?\x22\n"
Escape-последовательность | Юникод | Литеральные символы, помещенные в строку |
---|---|---|
\0 | U+0000 | нулевой символ [2] [3] (обычно как частный случай восьмеричной записи \oooo) |
\а | U + 0007 | тревога [4] [5] |
\б | U + 0008 | возврат назад [4] |
\ е | U+000C | подача формы [4] |
\п | U + 000A | перевод строки [4] (или новая строка в POSIX) |
\р | U + 000D | возврат каретки [4] (или новая строка в Mac OS 9 и более ранних версиях) |
\ т | U + 0009 | горизонтальная вкладка [4] |
\v | U + 000B | вертикальная вкладка [4] |
\и | U + 001B | escape-символ [5] ( ССЗ , [6] лязг и тсс ) |
\в#### | U+#### | 16-битный символ Юникода , где #### — четыре шестнадцатеричные цифры. [3] |
\В######## | У+###### | 32-битный символ Юникода, где ######## — восемь шестнадцатеричных цифр (ширина символьного пространства Юникода в настоящее время составляет всего 21 бит, поэтому первые две шестнадцатеричные цифры всегда будут равны нулю) |
\в{######} | У+###### | 21-битный символ Юникода, где ###### — переменное количество шестнадцатеричных цифр. |
\х## | Зависит от кодировки [б] | Спецификация 8-битного символа, где # — шестнадцатеричная цифра. Длина шестнадцатеричной escape-последовательности не ограничивается двумя цифрами, а имеет произвольную длину. [4] |
\ооо | Зависит от кодировки [б] | Спецификация 8-битного символа, где o — восьмеричная цифра. [4] |
\" | U + 0022 | двойная кавычка ("") [4] |
\& | несимвол, используемый для разделения числовых escape-символов в Haskell [2] | |
\' | U + 0027 | одинарная кавычка (') [4] |
\\ | U + 005C | обратная косая черта (\) [4] |
\? | U + 003F | вопросительный знак (?) [4] |
Примечание. Не все последовательности в списке поддерживаются всеми анализаторами, и могут существовать другие escape-последовательности, которых нет в списке.
Вложенное экранирование
[ редактировать ]Когда код на одном языке программирования встроен в другой, для встроенных строк может потребоваться несколько уровней экранирования. Это особенно часто встречается в регулярных выражениях и SQL-запросах на других языках или в других языках внутри сценариев оболочки. Такое двойное экранирование часто сложно читать и писать.
Неправильное заключение вложенных строк в кавычки может представлять собой уязвимость безопасности. При использовании ненадежных данных, например в полях данных SQL-запроса, следует использовать подготовленные операторы , чтобы предотвратить атаку путем внедрения кода . В версиях PHP со 2 по 5.3 существовала функция, называемая магическими кавычками , которая автоматически экранировала строки (для удобства и безопасности), но из-за проблем была удалена, начиная с версии 5.4.
Необработанные строки
[ редактировать ]Некоторые языки предоставляют метод указания того, что литерал должен обрабатываться без какой-либо интерпретации, специфичной для языка. Это позволяет избежать необходимости экранирования и дает более разборчивые строки.
Необработанные строки особенно полезны, когда необходимо экранировать общий символ, особенно в регулярных выражениях (вложенных в строковые литералы), где обратная косая черта \
широко используется, а также в путях DOS/Windows , где в качестве разделителя пути используется обратная косая черта. Обилие обратных косых черт известно как синдром наклоненной зубочистки , и его можно уменьшить, используя необработанные строки. Сравните экранированные и необработанные пути в C#:
"The Windows path is C:\\Foo\\Bar\\Baz\\"
@"The Windows path is C:\Foo\Bar\Baz\"
Крайние примеры возникают, когда они объединены: пути по единому соглашению об именах начинаются с \\
, и, таким образом, экранированное регулярное выражение, соответствующее UNC-имени, начинается с 8 обратных косых черт, "\\\\\\\\"
, из-за необходимости экранирования строки и регулярного выражения. Использование необработанных строк уменьшает это число до 4 (экранирование в регулярном выражении), как в C#. @"\\\\"
.
В документах XML разделы CDATA позволяют использовать такие символы, как & и <, без того, чтобы синтаксический анализатор XML пытался интерпретировать их как часть структуры самого документа. документа Это может быть полезно при включении обычного текста и кода сценариев, чтобы сохранить правильность формата .
<![CDATA[ if (path!=null && depth<2) { add(path); } ]]>
Многострочные строковые литералы
[ редактировать ]Во многих языках строковые литералы могут содержать буквальные символы новой строки, занимающие несколько строк. В качестве альтернативы, символы новой строки можно экранировать, чаще всего так: \n
. Например:
echo 'foo
bar'
и
echo -e "foo\nbar"
оба действительны bash, производя:
foo bar
Языки, допускающие буквальный перевод строки, включают bash, Lua, Perl, PHP, R и Tcl. В некоторых других языках строковые литералы не могут включать символы новой строки.
Две проблемы с многострочными строковыми литералами — это начало и конец новой строки, а также отступы. Если начальный или конечный разделители находятся на отдельных строках, появляются дополнительные символы новой строки, а если нет, то разделитель затрудняет чтение строки, особенно для первой строки, отступ которой часто отличается от остальных. Кроме того, литерал не должен иметь отступов, поскольку сохраняются ведущие пробелы — это нарушает поток кода, если литерал встречается в коде с отступом.
Наиболее распространенным решением этих проблем являются документа строковые литералы в стиле . Формально говоря, здесь документ — это не строковый литерал, а потоковый или файловый литерал. Они возникают в сценариях оболочки и позволяют использовать литерал в качестве входных данных для внешней команды. Открывающий разделитель <<END
где END
может быть любым словом, а закрывающим разделителем является END
на отдельной строке, служащей границей контента – <<
связано с перенаправлением стандартного ввода из литерала. Поскольку разделитель является произвольным, это также позволяет избежать проблемы столкновения разделителей. Они также позволяют удалять начальные вкладки с помощью вариантного синтаксиса. <<-END
хотя ведущие пробелы не удаляются. Тот же синтаксис с тех пор был принят для многострочных строковых литералов во многих языках, особенно в Perl, и здесь они также называются документами и сохраняют синтаксис, несмотря на то, что они являются строками и не требуют перенаправления. Как и в случае с другими строковыми литералами, для них иногда может быть указано другое поведение, например интерполяция переменных.
Python, чьи обычные строковые литералы не допускают буквальных символов новой строки, вместо этого имеет специальную форму строк, предназначенную для многострочных литералов, называемую тройными кавычками . Они используют тройной разделитель либо '''
или """
. Эти литералы особенно используются для встроенной документации, известной как строки документации .
Tcl допускает буквальные символы новой строки в строках и не имеет специального синтаксиса для работы с многострочными строками, хотя разделители могут размещаться в строках сами по себе, а начальные и конечные символы новой строки удаляются с помощью string trim
, пока string map
можно использовать для удаления вмятин.
Конкатенация строковых букв
[ редактировать ]В некоторых языках предусмотрена конкатенация строковых литералов , при которой соседние строковые литералы неявно объединяются в один литерал во время компиляции. Это особенность C, [7] [8] С++, [9] Д, [10] Руби, [11] и Питон, [12] который скопировал его из C. [13] Примечательно, что эта конкатенация происходит во время компиляции, во время лексического анализа (как этап после первоначальной токенизации) и контрастирует как с конкатенацией строк во время выполнения (обычно с +
оператор) [14] и конкатенация во время свертывания констант , которая происходит во время компиляции, но на более позднем этапе (после анализа фразы или «синтаксического анализа»). Большинство языков, таких как C#, Java [15] и Perl не поддерживают неявную конкатенацию строковых литералов, а вместо этого требуют явной конкатенации, например, с помощью +
оператор (это также возможно в D и Python, но запрещено в C/C++ – см. ниже); в этом случае конкатенация может происходить во время компиляции посредством свертывания констант или может быть отложена до времени выполнения.
Мотивация
[ редактировать ]В языке C, откуда возникли это понятие и термин, конкатенация строковых литералов была введена по двум причинам: [16]
- Чтобы длинные строки могли занимать несколько строк с правильными отступами в отличие от продолжения строки, которое разрушает схему отступов; и
- Разрешить создание строковых литералов с помощью макросов (посредством создания строк ). [17]
На практике это позволяет осуществлять конкатенацию строк на ранних этапах компиляции («перевод», в частности, как часть лексического анализа), без необходимости анализа фраз или постоянного свертывания. Например, следующие допустимы для C/C++:
char *s = "hello, " "world";
printf("hello, " "world");
Однако следующее недействительно:
char *s = "hello, " + "world";
printf("hello, " + "world");
Это связано с тем, что строковые литералы имеют тип массива , char [n]
(С) или const char [n]
(C++), который нельзя добавить; в большинстве других языков это не ограничение.
Это особенно важно при использовании в сочетании с препроцессором C , чтобы обеспечить возможность вычисления строк после предварительной обработки, особенно в макросах. [13] В качестве простого примера:
char *file_and_message = __FILE__ ": message";
будет (если файл называется ac) расширится до:
char *file_and_message = "a.c" ": message";
который затем объединяется, что эквивалентно:
char *file_and_message = "a.c: message";
Распространенным вариантом использования является создание строк формата printf или scanf , где спецификаторы формата задаются макросами. [18] [19]
В более сложном примере используется преобразование целых чисел в строку (с помощью препроцессора) для определения макроса, который расширяется до последовательности строковых литералов, которые затем объединяются в один строковый литерал с именем файла и номером строки: [20]
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define AT __FILE__ ":" TOSTRING(__LINE__)
Помимо синтаксических требований C/C++, неявная конкатенация представляет собой форму синтаксического сахара , упрощающую разделение строковых литералов на несколько строк, избегая необходимости продолжения строки (посредством обратной косой черты) и позволяя добавлять комментарии к частям строк. Например, в Python регулярное выражение можно прокомментировать следующим образом: [21]
re.compile("[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
Проблемы
[ редактировать ]Неявная конкатенация строк не требуется современными компиляторами, которые реализуют свертывание констант и вызывают труднообнаружимые ошибки из-за непреднамеренной конкатенации из-за пропуска запятой, особенно в вертикальных списках строк, например:
l = ['foo',
'bar'
'zork']
Соответственно, он не используется в большинстве языков, и его было предложено исключить из D. [22] и Питон. [13] Однако удаление этой функции нарушает обратную совместимость, а замена ее оператором конкатенации приводит к проблемам приоритета: конкатенация строковых литералов происходит во время лексического анализа, до вычисления оператора, но конкатенация с помощью явного оператора происходит одновременно с другими операторами, следовательно, приоритет Это проблема, потенциально требующая скобок для обеспечения желаемого порядка вычислений.
Более тонкая проблема заключается в том, что в C и C++ [23] существуют разные типы строковых литералов, и их объединение имеет поведение, определяемое реализацией, что представляет потенциальную угрозу безопасности. [24]
Различные виды струн
[ редактировать ]Некоторые языки предоставляют более одного вида литералов, которые имеют разное поведение. Это особенно используется для обозначения необработанных строк (без экранирования) или для отключения или включения интерполяции переменных, но имеет и другие применения, например, для различения наборов символов. Чаще всего это делается путем изменения символа цитирования или добавления префикса или суффикса. Это сравнимо с префиксами и суффиксами целочисленных литералов , например, для обозначения шестнадцатеричных чисел или длинных целых чисел.
Один из самых старых примеров — сценарии оболочки, где одинарные кавычки обозначают необработанную строку или «литеральную строку», а двойные кавычки содержат escape-последовательности и интерполяцию переменных.
Например, в Python необработанным строкам предшествует символ r
или R
- сравнивать 'C:\\Windows'
с r'C:\Windows'
(однако необработанная строка Python не может заканчиваться нечетным количеством обратных косых черт). Python 2 также различает два типа строк: 8-битные строки ASCII («байты») (по умолчанию), явно обозначаемые b
или B
префикс и строки Юникода, обозначенные значком u
или U
префикс. [25] в то время как в Python 3 строки по умолчанию имеют Unicode, а байты представляют собой отдельный bytes
тип, который при инициализации с кавычками должен иметь префикс b
.
Нотация C# для необработанных строк называется @-кавычками.
@"C:\Foo\Bar\Baz\"
Хотя это отключает экранирование, оно позволяет удвоить кавычки, что позволяет представлять кавычки внутри строки:
@"I said, ""Hello there."""
C++11 допускает необработанные строки, строки Юникода (UTF-8, UTF-16 и UTF-32) и строки широких символов, определяемые префиксами. Он также добавляет литералы для существующего C++. string
, который обычно предпочтительнее существующих строк в стиле C.
В Tcl строки, разделенные скобками, являются буквальными, а строки, разделенные кавычками, имеют экранирование и интерполяцию.
В Perl имеется большое разнообразие строк, которые более формально считаются операторами и известны как кавычки и операторы, подобные кавычкам . К ним относятся как обычный синтаксис (фиксированные разделители), так и общий синтаксис, позволяющий выбирать разделители; к ним относятся: [26]
'' "" `` // m// qr// s/// y///
q{} qq{} qx{} qw{} m{} qr{} s{}{} tr{}{} y{}{}
REXX использует символы-суффиксы для указания символов или строк, используя их шестнадцатеричный или двоичный код. Например,
'20'x
"0010 0000"b
"00100000"b
все дают пробел , избегая вызова функции X2C(20)
.
Строковая интерполяция
[ редактировать ]В некоторых языках строковые литералы могут содержать заполнители, ссылающиеся на переменные или выражения в текущем контексте , которые оцениваются (обычно во время выполнения). Это называется интерполяцией переменных или, в более общем смысле, интерполяцией строк . Языки, поддерживающие интерполяцию, обычно различают строковые литералы, которые интерполируются, от неинтерполируемых. Например, в sh-совместимых оболочках Unix (а также Perl и Ruby) строки в двойных кавычках (разделенных кавычками ") интерполируются, а строки в одинарных кавычках (разделенных апострофом ") — нет. Строковые литералы иногда называют «необработанными строками», но это отличается от «необработанной строки» в смысле экранирования. Например, в Python это строка с префиксом. r
или R
не имеет экранирования или интерполяции, обычная строка (без префикса) имеет экранирование, но не имеет интерполяции, а строка с префиксом f
или F
имеет экранирование и интерполяцию.
Например, следующий код Perl :
$name = "Nancy";
$greeting = "Hello World";
print "$name said $greeting to the crowd of people.";
производит вывод:
Nancy said Hello World to the crowd of people.
В этом случае метасимвол ($) (не путать с символом в операторе присваивания переменной) интерпретируется как указание на интерполяцию переменной и требует некоторого экранирования, если его нужно вывести буквально.
Этому следует противопоставить printf
функция, которая выдает тот же результат, используя такие обозначения, как:
printf "%s said %s to the crowd of people.", $name, $greeting;
но не выполняет интерполяцию: %s
является заполнителем в строке формата printf , но сами переменные находятся за пределами строки.
Это контрастирует с «необработанными» строками:
print '$name said $greeting to the crowd of people.';
которые производят вывод, например:
$name said $greeting to the crowd of people.
Здесь символы $ не являются метасимволами и не интерпретируются как имеющие какое-либо значение, кроме обычного текста.
Встраивание исходного кода в строковые литералы
[ редактировать ]Языки, которым не хватает гибкости в указании строковых литералов, делают особенно громоздким написание программного кода, генерирующего другой программный код. Это особенно верно, когда язык генерации такой же или похож на язык вывода.
Например:
- написание кода для создания квинов
- создание языка вывода из веб-шаблона ;
- использование XSLT для генерации XSLT или SQL для генерации большего количества SQL
- создание представления документа в формате PostScript для целей печати из приложения обработки документов, написанного на C или каком-либо другом языке.
- написание шейдеров
Тем не менее, некоторые языки особенно хорошо приспособлены для создания такого рода самоподобного вывода, особенно те, которые поддерживают несколько вариантов предотвращения столкновения разделителей.
Использование строковых литералов в качестве кода, генерирующего другой код, может иметь неблагоприятные последствия для безопасности, особенно если выходные данные хотя бы частично основаны на ненадежном пользовательском вводе. Это особенно актуально в случае веб-приложений, где злонамеренные пользователи могут воспользоваться такими слабостями, чтобы подорвать работу приложения, например, путем проведения атаки с помощью SQL-инъекции .
См. также
[ редактировать ]Примечания
[ редактировать ]- ^ Приведенное здесь регулярное выражение само по себе не заключено в кавычки и не экранировано, чтобы избежать путаницы.
- ^ Jump up to: а б Поскольку эта escape-последовательность представляет конкретную кодовую единицу , а не конкретный символ, то, какую кодовую точку (если таковая имеется) она представляет, зависит от кодировки строкового литерала, в котором она находится.
Ссылки
[ редактировать ]- ^ «Грамматика ANSI C (Lex)» . Лю.се. Проверено 22 июня 2016 г.
- ^ Jump up to: а б «Приложение Б. Символы, строки и правила экранирования» . Realworldhaskell.org . Проверено 22 июня 2016 г.
- ^ Jump up to: а б "Нить" . сайт mozilla.org . Проверено 22 июня 2016 г.
- ^ Jump up to: а б с д и ж г час я дж к л м «Escape-последовательности (C)» . microsoft.com . Проверено 22 июня 2016 г.
- ^ Jump up to: а б «Обоснование международного стандарта — языки программирования — C» (PDF) . 5.10. Апрель 2003 г., стр. 52, 153–154, 159. Архивировано (PDF) из оригинала 6 июня 2016 г. Проверено 17 октября 2010 г.
- ^ «6.35 Символ <ESC> в константах» , Руководство GCC 4.8.2 , получено 8 марта 2014 г.
- ^ Проект стандарта C11 , Проект комитета WG14 N1570 — 12 апреля 2011 г. , 5.1.1.2 Этапы перевода, стр. 11: «6. Токены соседних строковых литералов объединяются».
- ^ Синтаксис C: конкатенация строковых литералов
- ^ C++11 , Проект стандарта «Рабочий проект стандарта языка программирования C++» (PDF) . , 2.2 Фазы трансляции [lex.phases], с. 17: «6. Токены соседних строковых литералов объединяются». и 2.14.5 Строковые литералы [lex.string], примечание 13, с. 28–29: «На этапе трансляции 6 (2.2) соседние строковые литералы объединяются».
- ^ D Язык программирования , Лексический анализ , «Строковые литералы»: «Соседние строки объединяются с помощью оператора ~ или путем простого сопоставления:»
- ^ Ruby: Язык программирования Ruby , Язык программирования Ruby, 19 октября 2017 г. , получено 19 октября 2017 г.
- ^ Справочник по языку Python, 2. Лексический анализ, 2.4.2. Конкатенация строковых литералов : «Допускаются несколько соседних строковых литералов (разделенных пробелами), возможно, с использованием разных соглашений о кавычках, и их значение такое же, как и их конкатенация».
- ^ Jump up to: а б с Идеи Python, « Неявная конкатенация строковых литералов считается вредной? », Гвидо ван Россум, 10 мая 2013 г.
- ^ Справочник по языку Python, 2. Лексический анализ, 2.4.2. Конкатенация строковых литералов : «Обратите внимание, что эта функция определена на синтаксическом уровне, но реализована во время компиляции. Оператор «+» должен использоваться для объединения строковых выражений во время выполнения».
- ^ «Строки (Учебные пособия по Java™ > Изучение языка Java > Числа и строки)» . Docs.oracle.com . 28 февраля 2012 г. Проверено 22 июня 2016 г.
- ^ Обоснование языка программирования ANSI C. Силиконовый пресс. 1990. с. 31 . ISBN 0-929306-07-4 . , 3.1.4 Строковые литералы : «Длинную строку можно продолжить на несколько строк, используя продолжение строки с обратной косой чертой и новой строкой, но эта практика требует, чтобы продолжение строки начиналось с первой позиции следующей строки. Для обеспечения большей гибкости компоновки и для решения некоторых проблем предварительной обработки (см. §3.8.3) Комитет ввел конкатенацию строковых литералов, которые соединяются подряд (без нулевого символа в середине), образуя один объединенный строковый литерал. Это дополнение. в язык C позволяет программисту расширить строковый литерал за пределы физической строки без необходимости использования механизма обратной косой черты и новой строки и тем самым разрушения схемы отступов программы. Явный оператор конкатенации не был введен, поскольку конкатенация представляет собой оператор. лексическая конструкция, а не операция времени выполнения».
- ^ Обоснование языка программирования ANSI C. Силиконовый пресс. 1990. с. 6566 . ISBN 0-929306-07-4 . , 3.8.3.2 Оператор # : «Оператор # был введен для создания строк. Его можно использовать только в расширении #define. Он приводит к замене следующего имени формального параметра строковым литералом, сформированным путем преобразования в строку фактического токена аргумента. Последовательность. В сочетании с конкатенацией строковых литералов (см. §3.1.4) использование этого оператора позволяет создавать строки так же эффективно, как и путем замены идентификатора внутри строки. Пример в Стандарте иллюстрирует эту возможность.
- ^ Журнал пользователей C/C++, том 19, с. 50
- ^ «python — зачем разрешать конкатенацию строковых литералов?» . Переполнение стека . Проверено 22 июня 2016 г.
- ^ «LINE__ в строку (stringify) с использованием директив препроцессора» . Decompile.com . 12 октября 2006 г. Проверено 22 июня 2016 г.
- ^ Справочник по языку Python, 2. Лексический анализ, 2.4.2. Конкатенация строковых литералов : «Эту функцию можно использовать для уменьшения количества необходимых обратных косых черт, для удобного разделения длинных строк на длинные строки или даже для добавления комментариев к частям строк, например:
- ^ Система отслеживания проблем DLang - Проблема 3827 - Предупредить и затем отказаться от неявной конкатенации соседних строковых литералов.
- ^ C++11 , Проект стандарта «Рабочий проект стандарта языка программирования C++» (PDF) . , 2.14.5 Строковые литералы [lex.string], примечание 13, с. 28–29: «Любые другие объединения условно поддерживаются поведением, определяемым реализацией».
- ^ «STR10-C. Не объединяйте строковые литералы разных типов — Безопасное кодирование — Стандарты безопасного кодирования CERT» . Архивировано из оригинала 14 июля 2014 года . Проверено 3 июля 2014 г.
- ^ «2. Лексический анализ — документация Python 2.7.12rc1» . python.org . Проверено 22 июня 2016 г.
- ^ «perlop — perldoc.perl.org» . perl.org . Проверено 22 июня 2016 г.