С++: есть ли стандартное определение конца строки в многострочной строковой константе?

Если у меня есть многострочная строковая константа С++ 11, такая как

R"""line 1
line 2
line3"""

Определено ли, из каких символов состоит терминатор/разделитель строки?


person Mark Harrison    schedule 05.10.2016    source источник
comment
Он состоит из того, что находится в исходном файле. Это буквально: содержимое буквально то, что вы ввели. Для сервера с четко определенными требованиями EOL, такими как HTTP, этого недостаточно: вы должны использовать \r\n для HTTP, почты и т. д.   -  person user207421    schedule 06.10.2016
comment
Это только текст или это строковый литерал? Разве не должны быть какие-то круглые скобки, чтобы литерал необработанной строки работал? Возможно, что-то, начинающееся с R"(""line 1?   -  person wally    schedule 06.10.2016
comment
@Cheersandhth.-Alf - Не все посетители сайта имеют 128 тыс. представителей или являются экспертами в этой области. Пока вопрос относится к теме, а не дублируется, он приемлем на SO независимо от представителя спрашивающего.   -  person owacoder    schedule 06.10.2016
comment
Механически я считаю, что это \n, который представляет 0x0A в самом программном коде (точно так же, как новые строки в других строковых константах), но разные среды будут переводить его в свои собственные новые строки (например, CRLF в Windows).   -  person Justin Time - Reinstate Monica    schedule 06.10.2016
comment
По этому вопросу существует (или, по крайней мере, было) фактическое расхождение в реализации. Вряд ли это совсем новичок, когда люди, читающие стандарты и зарабатывающие на жизнь написанием компиляторов, не согласны друг с другом.   -  person T.C.    schedule 06.10.2016
comment
@Cheersandhth.-Alf: Достаточно ли высока моя репутация в 146 000, чтобы я мог с вами не согласиться?   -  person Keith Thompson    schedule 06.10.2016
comment
@Cheersandhth.-Alf: В частности, я не согласен с тем, что это вопрос уровня новичка, учитывая, что сам стандарт непоследователен.   -  person Keith Thompson    schedule 06.10.2016
comment
@KeithThompson: стандарт не противоречит. Это просто туманно сформулировано. По-видимому, никто не подумал, что стоит тратить время на уточнение этого или, если уж на то пошло, решение DR, потому что это базовые, тривиальные вещи уровня новичка, вещи, которые знают все, и никто на уровне тех, кто реализует составители (основная читательская аудитория) не согласились бы.   -  person Cheers and hth. - Alf    schedule 06.10.2016
comment
@Cheersandhth.-Alf: Я не согласен. Стандарт прямо говорит, что любые преобразования, выполненные на этапах 1 и 2, отменяются. Эти преобразования явно включают введение символов новой строки. Нормативная формулировка недвусмысленна, и если вы пропустите индикаторы конца строки, которые не являются последовательностями символов, это даже имеет смысл (вам может понадобиться захотеть пары CR-LF в Windows). И кто-то подумал, что стоит подать DR, который не решается более 3 лет. Я не говорю, что вы не правы, я просто предполагаю, что ваш вывод не так очевиден, как вам кажется.   -  person Keith Thompson    schedule 06.10.2016
comment
@KeithThompson: Насколько я вижу, вы просто неправильно читаете слово «преобразование». С такой плохой интерпретацией человек в конечном итоге думает, что что-то явно заявлено, хотя на самом деле это просто следствие плохой интерпретации. Его можно признать плохим, так как он приводит к несогласованности. Вместо этого хорошая интерпретация заключается в том, что непосредственно следующий список соответствующих преобразований — это то, к чему здесь относится слово «преобразования». И все эти преобразования направлены на то, чтобы сделать обратную косую черту фактически необработанной, что и касается необработанных литералов. /Это имеет смысл.   -  person Cheers and hth. - Alf    schedule 06.10.2016
comment
@Cheersandhth.-Alf: Думаю, ты, наверное, прав. Но я все еще думаю, что формулировка небрежна и может быть разумным недоразумением. В нем говорится, что любые преобразования (выделено мной) отменены. Предложение в скобках после этого (триграфы, универсальные имена символов и объединение строк), по-видимому, предназначено для определения того, о каких преобразованиях идет речь, но недостаточно ясно, что это исчерпывающий список. Совершенно разумно рассматривать сопоставление физических исходных символов с базовым исходным набором символов как преобразование.   -  person Keith Thompson    schedule 06.10.2016
comment
@Cheersandhth.-Alf: я переписал свой ответ.   -  person Keith Thompson    schedule 06.10.2016
comment
@KeithThompson: мне нравится, проголосовал.   -  person Cheers and hth. - Alf    schedule 06.10.2016
comment
Кто-нибудь проверял, как это реализовано в реальных компиляторах, желательно в разных ОС?   -  person Mr Lister    schedule 06.10.2016
comment
Я отклонил этот вопрос как ненастоящий вопрос, учитывая высокий балл репутации ОП и предполагаемый опыт работы как очень компетентного человека, в то время как вопрос является вопросом новичка о действительно базовых вещах. Оригинальный комментарий был удален модами SO. В комментарии выше утверждается, что вопрос на самом деле не является основным, потому что по крайней мере у одного компилятора была ошибка в этой области. Для меня это нонсенс.   -  person Cheers and hth. - Alf    schedule 07.10.2016


Ответы (3)


Цель состоит в том, чтобы новая строка в необработанном строковом литерале сопоставлялась с одним символом '\n'. Это намерение выражено не так ясно, как следовало бы, что привело к некоторой путанице.

Цитаты относятся к стандарту ISO C++ 2011 года.

Во-первых, вот доказательство того, что оно соответствует одному символу '\n'.

В примечании к параграфу 4 раздела 2.14.5 [lex.string] говорится:

[ Примечание. Новая строка исходного файла в необработанном строковом литерале приводит к новой строке в результирующем выполнении string-literal. Предполагая отсутствие пробелов в начале строк в следующем примере, утверждение завершится успешно:

    const char *p = R"(a\
    b
    c)";
    assert(std::strcmp(p, "a\\\nb\nc") == 0);

конец примечания ]

Здесь четко указано, что новая строка отображается на один символ '\n'. Это также соответствует наблюдаемому поведению g++ 6.2.0 и clang++ 3.8.1 (тесты, выполненные в системе Linux с использованием исходных файлов с окончаниями строк в стиле Unix и Windows).

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

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

В параграфе 3 раздела 2.5 [lex.pptoken] говорится (выделено мной):

Между начальными и конечными символами двойных кавычек необработанной строки любые преобразования, выполненные на этапах 1 и 2 (триграфы, универсальные имена символов и объединение строк) отменяются. ; эта реверсия должна применяться до того, как будет идентифицирована любая d-char, r-char или разделительная скобка.

Фазы перевода указаны в 2.2 [lex.phases]. На этапе 1:

Символы физического исходного файла сопоставляются способом, определяемым реализацией, с базовым исходным набором символов (с введением символов новой строки для индикаторов конца строки), если это необходимо.

Если мы предположим, что сопоставление символов физического исходного файла с базовым набором символов и введение символов новой строки являются «преобразованиями», мы могли бы разумно заключить, что, например, новая строка в середине необработанного строкового литерала в исходном файле формата Windows должен быть эквивалентен последовательности \r\n. (Я могу представить, что это полезно для кода, специфичного для Windows.)

(Эта интерпретация действительно приводит к проблемам с системами, в которых индикатор конца строки не является последовательностью символов, например, где каждая строка представляет собой запись фиксированной ширины. Такие системы в наши дни редкость.)

Как указывает ответ "Cheers and hth. - Alf", существует открытый Отчет о дефекте для этой проблемы. Оно было подано в 2013 году и до сих пор не решено.

Лично я думаю, что корень путаницы в слове «любой» (курсив добавлен, как и раньше):

Между начальными и конечными символами двойных кавычек необработанной строки любые преобразования, выполненные на этапах 1 и 2 (триграфы, универсальные имена символов и объединение строк) отменяются. ; эта реверсия должна применяться до того, как будет идентифицирована любая d-char, r-char или разделительная скобка.

Конечно, сопоставление физических символов исходного файла с базовым набором исходных символов можно разумно рассматривать как преобразование. Предложение в скобках «(триграфы, универсальные имена символов и соединение строк)», по-видимому, предназначено для указания какие преобразования должны быть отменены, но это либо попытка изменить значение слова « преобразования» (которые стандарт формально не определяет) или противоречит использованию слова «любой».

Я полагаю, что замена слова «любой» на «определенный» намного яснее отразила бы очевидное намерение:

Между начальными и конечными символами двойных кавычек необработанной строки некоторые преобразования, выполненные на этапах 1 и 2 (триграфы, универсальные имена символов и объединение строк), отменяются; эта реверсия должна применяться до того, как будет идентифицирована любая d-char, r-char или разделительная скобка.

Эта формулировка делает более ясным, что «триграфы, универсальные имена символов и сращивание строк» ​​являются единственными преобразованиями, которые должны быть отменены. (Не все, что делается на этапах перевода 1 и 2, отменяется, только те определенные перечисленные преобразования.)

person Keith Thompson    schedule 06.10.2016
comment
Обратите внимание, что интерпретация ради аргументов полностью терпит неудачу для файловых систем, где строки являются записями, то есть где нет данных, обозначающих новую строку. Я очень давно не пользовался такой системой, но, насколько я помню, MPE/IV на HP 3000 был именно таким. Я смутно припоминаю, что читал, что VAX тоже был этим, но хотя я использовал машины VAX в студенческие годы, я не могу вспомнить, извините. Но, главное, правила C++ не могут обойтись без преобразования символов новой строки в \n. Потому что иначе не будет работать на этих системах. - person Cheers and hth. - Alf; 06.10.2016
comment
Также обратите внимание, что по этому поводу существует проблема с открытым базовым языком, о которой упоминается в моем ответе. - person Cheers and hth. - Alf; 06.10.2016
comment
Вы писали выше: здесь четко указано, что новая строка сопоставляется с одним символом '\n'. Я согласен с вашим утверждением за одним единственным исключением: утверждение в стандарте сбивает с толку, поскольку в нем используется термин новая строка, как если бы он вводил символ новой строки, а не escape-последовательность `\n'. Не могли бы вы прокомментировать это? - person Belloc; 29.03.2020
comment
@Belloc - я не уверен, что понял твой комментарий. Разрыв строки в необработанном строковом литерале приводит к символу новой строки. Он не создает управляющую последовательность \n, но эквивалентен управляющей последовательности \n. - person Keith Thompson; 30.03.2020
comment
Хорошо, я думаю, что могу понять, что вы говорите. Но где именно в стандарте говорится, что символ новой строки сопоставляется с одним символом \n, как вы написали выше? Я уверен, вы знаете, что [lex.string]/3 это просто примечание. - person Belloc; 30.03.2020
comment
@Belloc Весь смысл цитируемого отчета о дефектах заключается в том, что стандарт не четко указывает это в нормативном тексте. Сноска подразумевает, что это было намерением. (Я не проверял, разрешают ли это более поздние редакции стандарта.) - person Keith Thompson; 30.03.2020
comment
После подробного поиска в Стандарте я нашел кое-что (таблица 9), что, по моему мнению, придает нормативный статус утверждению о том, что новая строка отображается на одиночный символ \n. Спасибо за ваши комментарии. - person Belloc; 31.03.2020
comment
@Belloc: В этой таблице сказано, что \n в символьном литерале представляет собой символ новой строки. Он ничего не говорит о необработанных строковых литералах. - person Keith Thompson; 31.03.2020

Стандарт, кажется, указывает, что:

R"""line 1
line 2
line3"""

эквивалентно:

"line 1\nline 2\nline3"

Из 2.14.5 строковых литералов стандарта C++11:

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

const char *p = R"(a\
b
c)";
assert(std::strcmp(p, "a\\\nb\nc") == 0);

конец примечания ]

5 [ Пример: Необработанная строка

R"a(
)\
a"
)a"

эквивалентно "\n)\\\na\"\n".

person R Sahu    schedule 06.10.2016
comment
Не понятно. Это говорит только о том, что новая строка сопоставляется с новой строкой. Не сказано, что произойдет, например, если исходный файл содержит \r. - person user207421; 06.10.2016
comment
@EJP: найдите этапы перевода в стандарте. Очевидно, вы думаете, что символы исходного файла превращаются непосредственно в литералы. Они не делают. - person Cheers and hth. - Alf; 06.10.2016
comment
@Cheersandhth.-Alf В этом нет ничего «очевидно». Это именно то, что я сказал в своем комментарии под вопросом. Я вполне могу ошибаться в этом, но убедительных доказательств здесь пока нигде не появилось. Этот ответ подходит ближе всего. Хоть что-то цитирует и цитирует. - person user207421; 06.10.2016
comment
@Cheersandhth.-Alf в 2.5 также говорит, что между начальными и конечными символами двойных кавычек необработанной строки любые преобразования, выполненные на этапах 1 и 2 (триграфы, универсальные имена символов и объединение строк), отменяются. - person harmic; 06.10.2016
comment
@harmic: Да, это из-за того, что необработанные литералы были добавлены к языку очень поздно. Это кладж, чтобы заставить \\ эффективно не обрабатываться в необработанном строковом литерале. Но с неудачной формулировкой. Его можно проанализировать, потребовав, чтобы он имел смысл. Имеет смысл, если список в скобках читать как исчерпывающий перечень возможных преобразований. Это теряет смысл, если преобразование интерпретируется как включающее в себя преобразование набора символов и конца строки, поскольку тогда один и тот же исходный код в разных кодировках может создавать разные программы. - person Cheers and hth. - Alf; 06.10.2016
comment
Кроме того, примеры и примечания ненормативный, что для меня оставляет некоторую двусмысленность - person harmic; 06.10.2016

Примечание: после публикации ответов вопрос существенно изменился. Остается только половина, а именно аспект чистого C++. Сетевой фокус в этом ответе касается исходного вопроса об отправке многострочной строки на сервер с четко определенными требованиями к концу строки. Я не гонюсь за эволюцией вопросов вообще.

Внутри программы стандартом C++ для новой строки является \n. Это также используется для новой строки в необработанном литерале. Для необработанных литералов нет специального соглашения.

Обычно \n соответствует переводу строки ASCII, значение которого равно 10.

Я не уверен, что это соответствует EBCDIC, но вы можете проверить это, если необходимо.

Однако в сети, у меня сложилось впечатление, что большинство протоколов используют возврат каретки ASCII плюс перевод строки, то есть 13, за которым следует 10. Это иногда называют CRLF, после аббревиатур ASCII CR для возврата каретки и LF для перевода строки. . Когда escape-последовательности С++ отображаются в ASCII, это просто \r\n в С++.

Вы должны соблюдать требования протокола, который вы используете.

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

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


По этому поводу существует активная проблема, Отчет о дефектах основного языка № 1655 Окончания строк в необработанных строковых литералах, представленный Майком Миллером 26 апреля 2013 г., где он спрашивает:

предполагается, что, например, CRLF в исходном строковом литерале должен быть представлен как символ новой строки или как исходные символы?

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

person Cheers and hth. - Alf    schedule 06.10.2016
comment
Это (напрямую) не отвечает на вопрос о необработанных строковых литералах. - person Tavian Barnes; 06.10.2016
comment
@TavianBarnes: стандарт C++ для новой строки — \n. Я не понимаю, как ты не смог это прочитать. - person Cheers and hth. - Alf; 06.10.2016
comment
@Alf Стандарт C++ предусматривает, что \n сопоставляется с переводом строки. Если в стандарте есть что-то, что отвечает на этот вопрос, или что «сопоставляет новую строку с \n», что не обязательно одно и то же, вы должны процитировать и процитировать это. - person user207421; 06.10.2016
comment
@Cheersandhth.-Alf Если вы считаете, что R""" """ (должна быть новая строка между """, но, конечно, она не будет отображаться в комментариях) и "\n" эквивалентны, вы должны сказать это явно (и желательно привести доказательства). - person Tavian Barnes; 06.10.2016
comment
@EJP: стандарт не говорит, что '\n' соответствует переводу строки. Он даже не упоминает символ перевода строки. В нем говорится, что '\n' отображается на новую строку. - person Keith Thompson; 06.10.2016
comment
Друзья Марка: глупо минусовать в отместку. Я не могу передать, насколько это чертовски глупо. Он, конечно же, не получит от меня никакой помощи, такой как помощь, которая привела к ответу, который он выбрал в качестве решения (изначально все было наоборот). - person Cheers and hth. - Alf; 08.10.2016