Избегая первой строки в строчном литерале С++ 11?
Необработанные строковые литералы в С++ 11 очень приятные, за исключением того, что очевидный способ их форматирования приводит к избыточной новой строке \n
в качестве первого символа.
Рассмотрим следующий пример:
some_code();
std::string text = R"(
This is the first line.
This is the second line.
This is the third line.
)";
more_code();
Очевидное обходное решение кажется настолько уродливым:
some_code();
std::string text = R"(This is the first line.
This is the second line.
This is the third line.
)";
more_code();
Кто-нибудь нашел элегантное решение?
Ответы
Ответ 1
Вы можете получить указатель на второй символ - пропустить ведущую новую строку - добавив 1 к const char*
, к которому автоматически преобразуется строковый литерал:
some_code();
std::string text = 1 + R"(
This is the first line.
This is the second line.
This is the third line.
)";
more_code();
IMHO, вышеупомянутое ошибочно ломается с отступом окружающего кода. Некоторые языки предоставляют встроенную или библиотечную функцию, которая делает что-то вроде:
- удаляет пустую ведущую строку, а
- смотрит на отступ второй строки и удаляет то же количество отступов из всех дальнейших строк
Это позволяет использовать:
some_code();
std::string text = unindent(R"(
This is the first line.
This is the second line.
This is the third line.
)");
more_code();
Запись, работающая во время выполнения, относительно проста (см. запустить на ideone.com)...
std::string unindent(const char* p)
{
std::string result;
if (p[0] == '\n') ++p;
const char* p_leading = p;
while (std::isspace(*p) && *p != '\n')
++p;
size_t leading_len = p - p_leading;
while (*p)
{
result += *p;
if (*p == '\n')
{
++p;
for (size_t i = 0; i < leading_len; ++i)
if (p[i] != p_leading[i])
goto dont_skip_leading;
p += leading_len;
}
else
++p;
dont_skip_leading: ;
}
return result;
}
... но было бы гораздо лучше выполнить обработку во время компиляции. Я наткнулся на этот пост, в котором упоминается библиотека "constexpr_string" , которая иллюстрирует аналогичную функциональность, но не имела трещина на нем еще....
Ответ 2
Это, вероятно, не то, что вы хотите, но на всякий случай вы должны знать о конкатенации автоматической строки:
std::string text =
"This is the first line.\n"
"This is the second line.\n"
"This is the third line.\n";
Ответ 3
Я рекомендую ответить @Brian, особенно если вам нужно иметь только несколько строк текста или то, что вы можете обрабатывать с помощью текстового редактора-fu. У меня есть альтернатива, если это не так.
std::string text =
"\
This is the first line." R"(
This is the second line.
This is the third line.)";
Живой пример
Строковые литералы строки могут конкатенироваться с "нормальными" строковыми литералами, как показано в коде. "\
в начале означает "исключить" символ "
из первой строки, поместив его в свою собственную строку.
Тем не менее, если бы я решил, я бы поместил такой лота-текст в отдельный файл и загрузил его во время выполнения. Нет никакого давления на вас, хотя: -).
Кроме того, это один из уродливых кодов, которые я написал в эти дни.
Ответ 4
Самое близкое, что я вижу:
std::string text = ""
R"(This is the first line.
This is the second line.
This is the third line.
)";
Было бы немного лучше, если бы пробел был разрешен в последовательности разделителя. Дайте или возьмите отступы:
std::string text = R"
(This is the first line.
This is the second line.
This is the third line.
)
";
Мой препроцессор позволит вам предупредить об этом, но, к сожалению, это немного бесполезно. Clang и GCC полностью отбрасываются.
Ответ 5
У меня была та же самая проблема, и я думаю, что следующее решение является лучшим из всех вышеперечисленных. Я надеюсь, что это будет полезно и для вас (см. Пример в комментарии):
/**
* Strips a multi-line string indentation prefix.
*
* Example:
* \code
* string s = R"(|line one
* |line two
* |line three
* |)"_multiline;
* std::cout << s;
* \endcode
*
* This prints three lines: @c "line one\nline two\nline three\n"
*
* @author Christian Parpart <[email protected]>
*/
inline std::string operator ""_multiline(const char* text, unsigned long size) {
if (!*text)
return {};
enum class State {
LineData,
SkipUntilPrefix,
};
constexpr char LF = '\n';
State state = State::LineData;
std::stringstream sstr;
char sep = *text++;
while (*text) {
switch (state) {
case State::LineData: {
if (*text == LF) {
state = State::SkipUntilPrefix;
sstr << *text++;
} else {
sstr << *text++;
}
break;
}
case State::SkipUntilPrefix: {
if (*text == sep) {
state = State::LineData;
text++;
} else {
text++;
}
break;
}
}
}
return sstr.str();
}
Ответ 6
Принятый ответ выдает предупреждение cppcoreguidelines-pro-bounds-constant-array-index из clang-tidy
. См. Pro.bounds: Профиль безопасности Bounds для деталей.
Если у вас нет std::span
но вы по крайней мере компилируете с С++ 17, подумайте:
constexpr auto text = std::string_view(R"(
This is the first line.
This is the second line.
This is the third line.
)").substr(1);
Основными преимуществами являются удобочитаемость (IMHO) и то, что вы можете включить это предупреждающее предупреждение в остальной части вашего кода.
Используя gcc
если кто-то непреднамеренно уменьшает необработанную строку до пустой строки, вы получаете ошибку компилятора (демо) с этим подходом, в то время как принятый подход либо ничего не дает (демо), либо в зависимости от настроек вашего компилятора "вне границ постоянной строки" предупреждение.
Ответ 7
Да, это раздражает. Возможно, должны быть необработанные литералы (R"PREFIX("
) и многострочные необработанные литералы (M"PREFIX
).
Я придумал эту альтернативу, которая почти описывает себя:
#include<iterator> // std::next
...
{
...
...
std::string atoms_text =
std::next/*_line*/(R"XYZ(
O123 12.4830720891 13.1055820441 9.5288258996
O123 13.1055820441 13.1055820441 9.5288258996
)XYZ");
assert( atoms_text[0] != '\n' );
...
}
Ограничения:
- Если необработанный литерал пуст, он сгенерирует недопустимую строку. Но это должно быть очевидно, чтобы определить.
- Если необработанный литерал не начинается с новой строки, вместо него будет использован первый символ.
-
std::next
является constexpr
только из С++ 17, затем вы можете использовать 1+(char const*)R"XYZ("
но это не так ясно и может выдавать предупреждение.
constexpr auto atom_text = 1 + (R"XYZ(
O123 12.4830720891 13.1055820441 9.5288258996
O123 13.1055820441 13.1055820441 9.5288258996
)XYZ");
Также никаких гарантий;). В конце концов, я не знаю, допустимо ли делать арифметику с указателями на статические данные.