C ++ 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();
有没有人找到一个优雅的解决方案?
答案 0 :(得分:19)
您可以通过向自动转换字符串文字的const char*
添加1来获取指向第二个字符的指针 - 跳过前导换行符:
some_code();
std::string text = 1 + R"(
This is the first line.
This is the second line.
This is the third line.
)";
more_code();
允许使用如下:
some_code();
std::string text = unindent(R"(
This is the first line.
This is the second line.
This is the third line.
)");
more_code();
编写一个在运行时运行的程序相对简单(参见run at 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;
}
...但是在编译时进行处理会好得多。我偶然发现this post提到a "constexpr_string" library,说明了类似的功能,但还没有解决它......
答案 1 :(得分:4)
这可能不是你想要的,但为了以防万一,你应该知道自动字符串文字串联:
std::string text =
"This is the first line.\n"
"This is the second line.\n"
"This is the third line.\n";
答案 2 :(得分:3)
我推荐@Brian的答案,特别是如果你只需要几行文字,或者你可以用你的文字编辑器来处理它。如果不是这样的话,我有另一种选择。
std::string text =
"\
This is the first line." R"(
This is the second line.
This is the third line.)";
原始字符串文字仍然可以与&#34; normal&#34;连接。字符串文字,如代码中所示。一开始的"\
意味着&#34;消除&#34;第一行中的"
字符,将其放在自己的行中。
但是,如果我决定,我会把这么多的文本放到一个单独的文件中并在运行时加载它。对你没有压力: - )。
另外,这是我今天写的最丑陋的代码之一。
答案 3 :(得分:2)
我能看到的最近的是:
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完全被抛弃了。
答案 4 :(得分:0)
我遇到了同样的问题,我认为以下解决方案是上述所有解决方案中最好的。希望对您也有帮助(请参见注释中的示例):
/**
* Strips a multi-line string's 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 <christian@parpart.family>
*/
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();
}
答案 5 :(得分:0)
接受的答案会从clang-tidy
发出警告cppcoreguidelines-pro-bounds-constant-array-index。有关详细信息,请参见Pro.bounds: Bounds safety profile。
如果您没有std::span
,但至少要使用C ++ 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
还原为空字符串,则使用此方法会出现编译器错误(demo),而可接受的方法不会产生任何结果(demo )或根据您的编译器设置发出“常量字符串超出范围”警告。
答案 6 :(得分:0)
是的,这很烦人。也许应该有原始文字(R"PREFIX("
)和 multiline 原始文字(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
仅在C ++ 17中是constexpr
,您可以使用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");
此外,没有任何保证;)。毕竟,我不知道使用指向静态数据的指针进行算术是否合法。