C++如何从多行字符串中移除换行符

76

如何最高效地从std::string中删除换行符?


12
换行符是否应该位于特定位置,比如字符串的末尾? - Greg Hewgill
不;它可以在任何地方。 - shergill
13个回答

151
#include <algorithm>
#include <string>

std::string str;

str.erase(std::remove(str.begin(), str.end(), '\n'), str.cend());

std::remove的行为可能与您期望的不完全相同。

通常在调用remove后会调用容器的erase方法,该方法删除未指定的值并减少容器的物理大小以匹配其新的逻辑大小。

可以在这里查看相关说明。


7
如果存在来自其他平台的换行符,也许需要删除 '\r' 字符。再次调用 erase 和 std::remove 等函数对性能没有大影响。使用 std::remove_if 函数和谓词函数等替代方法可能会更慢。 - user180247
如果您的数据最初是从以文本(ASCII,非二进制)模式打开的文件加载的,我认为它会自动将所有换行约定转换为简单的'\n'。我正在寻找一个明确的参考来证实这一点。 - luke
这是关于fread()的内容,但我相信iostream读写具有相同的行为。 - luke
std::remove 参考文献中的关键句是:“通常在调用 remove 后会紧接着调用容器的 erase 方法,该方法会删除未指定的值并将容器的物理大小减小到与其新的逻辑大小相匹配。” - wcochran
2
最后一行的最后一个参数可以是 cend(),例如 str.erase(std::remove(str.begin(), str.end(), '\n'), str.cend()); - Oğuzhan Türk

14
如果期望换行符出现在字符串的末尾,则:
if (!s.empty() && s[s.length()-1] == '\n') {
    s.erase(s.length()-1);
}

如果字符串可以在任意位置包含许多换行符:

std::string::size_type i = 0;
while (i < s.length()) {
    i = s.find('\n', i);
    if (i == std::string:npos) {
        break;
    }
    s.erase(i);
}

3
第一个版本已经完美。第二个版本使用"std::erase(std::remove(XXX))"会更易用。 - Martin York
1
我从来没有对remove()的语义感到非常舒适,总是不得不查阅它,因为它并不明显。我的上述实现是简单直接的,但不是最有效的。如果效率很重要,需要稍微不同的解决方案。 - Greg Hewgill
1
问题是“什么是最有效的方法...”,所以我猜效率很重要;) - Pieter
1
这是您的代码第一部分的更新后的C++版本。 - Gabriel Staples

8
你应该使用“erase-remove惯用语”,查找'\n'。 这将适用于任何标准序列容器; 不仅限于string

5

这里有一个适用于DOS或Unix的换行符:

    void chomp( string &s)
    {
            int pos;
            if((pos=s.find('\n')) != string::npos)
                    s.erase(pos);
    }

6
if改为while循环,你就有一个相当不错的解决方案。 - CaptainBli

2
对edW的解决方案进行轻微修改,以删除所有现有的换行符。
void chomp(string &s){
size_t pos;
while (((pos=s.find('\n')) != string::npos))
    s.erase(pos,1);
}

请注意,size_t是用于pos的类型。这是因为npos对于不同的类型有不同的定义。例如,-1(无符号整数)和-1(无符号浮点数)并不相同,因为每种类型的最大大小不同。因此,即使它们的值都为-1,将int与size_t进行比较可能会返回false。

2
s.erase(std::remove(s.begin(), s.end(), '\n'), s.end());

1
另一种在for循环中执行的方法。
void rm_nl(string &s) {
    for (int p = s.find("\n"); p != (int) string::npos; p = s.find("\n"))
    s.erase(p,1);
}

使用方法:

string data = "\naaa\nbbb\nccc\nddd\n";
rm_nl(data); 
cout << data; // data = aaabbbcccddd

1

该代码从字符串str中删除所有换行符。

O(N)实现最好在SO上不带注释,在生产环境中带有注释。

unsigned shift=0;
for (unsigned i=0; i<length(str); ++i){
    if (str[i] == '\n') {
        ++shift;
    }else{
        str[i-shift] = str[i];
    }
}
str.resize(str.length() - shift);

1
 std::string some_str = SOME_VAL;
 if ( some_str.size() > 0 && some_str[some_str.length()-1] == '\n' ) 
  some_str.resize( some_str.length()-1 );

或(删除末尾的多个换行符)

some_str.resize( some_str.find_last_not_of(L"\n")+1 );

1

为了扩展@Greg Hewgill's answer,适用于C++11:

如果你只需要删除字符串末尾的换行符:

在C++98中可以这样实现:

if (!s.empty() && s[s.length()-1] == '\n') {
    s.erase(s.length()-1);
}

现在在C++11中可以这样做:

if (!s.empty() && s.back() == '\n') {
    s.pop_back();
}

如果需要的话,可以将其封装在一个函数中。请注意,我在这里仅通过指针传递它,这样当您将其作为参数传递并取其地址时,它会提醒您该字符串将在函数内部被原地修改

void remove_trailing_newline(std::string* str) 
{
    if (str->empty())
    {
        return;
    }

    if (str->back() == '\n') 
    {
        str->pop_back();
    }
}

// usage
std::string str = "some string\n";
remove_trailing_newline(&str);

什么是从std::string中删除“换行符”的最有效方法?
就最有效的方法而言,我需要进行速度测试/分析并查看。我会尝试回复您,并在此处运行一些速度测试,比较前两个答案和类似于我在这里所做的C样式方式:从C数组中删除元素。我将使用我的nanos()时间戳函数进行速度测试。
其他参考资料:
  1. 在这个参考维基中查看这些“新”的C++11函数:https://en.cppreference.com/w/cpp/string/basic_string
  2. https://en.cppreference.com/w/cpp/string/basic_string/empty
  3. https://en.cppreference.com/w/cpp/string/basic_string/back
  4. https://en.cppreference.com/w/cpp/string/basic_string/pop_back

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接