避免使用C ++ 11原始字符串文字中的第一个换行符?

时间:2014-07-22 05:15:42

标签: c++ c++11

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();

有没有人找到一个优雅的解决方案?

7 个答案:

答案 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.)";

Live example

原始字符串文字仍然可以与&#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' );
    ...
}

限制:

  1. 如果原始文字为空,将生成一个无效的字符串。但这应该很明显。
  2. 如果原始文字不以换行开头,它将吃掉第一个字符。
  3. 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");

此外,没有任何保证;)。毕竟,我不知道使用指向静态数据的指针进行算术是否合法。