ti-enxame.com

Substitua vários espaços por um espaço em uma sequência

Como eu faria algo em c++ semelhante ao seguinte código:

//Lang: Java
string.replaceAll("  ", " ");

Esse trecho de código substituiria todos os vários espaços em uma string por um único espaço.

22
evenodd
bool BothAreSpaces(char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); }

std::string::iterator new_end = std::unique(str.begin(), str.end(), BothAreSpaces);
str.erase(new_end, str.end());   

Como isso funciona. O std::unique possui duas formas. O primeiro formulário passa por um intervalo e remove duplicatas adjacentes. Portanto, a string "abbaaabbbb" se torna "abab". A segunda forma, que eu usei, usa um predicado que deve receber dois elementos e retornar verdadeiro se eles devem ser considerados duplicados. A função que escrevi, BothAreSpaces, serve a esse propósito. Determina exatamente o que o nome implica, que ambos os parâmetros são espaços. Então, quando combinado com std::unique, os espaços adjacentes duplicados são removidos.

Assim como std::remove e remove_if , std::unique na verdade não diminui o contêiner, apenas move os elementos no final para mais perto do início. Ele retorna um iterador para o novo fim de intervalo, para que você possa usá-lo para chamar a função erase , que é uma função membro da classe string.

Quebrando-o, a função apagar requer dois parâmetros, um iterador inicial e final para que um intervalo seja apagado. Para o primeiro parâmetro, estou passando o valor de retorno de std::unique, porque é onde eu quero começar a apagar. Para esse segundo parâmetro, estou passando o iterador final da string.

64
Benjamin Lindley

Então, tentei uma maneira com as expressões std :: remove_if & lambda - embora ainda pareça aos meus olhos mais fácil de seguir do que o código acima, ele não tem aquela coisa "uau legal, não sabia que você poderia fazer isso" para De qualquer forma, ainda o publico, mesmo que apenas para fins de aprendizado:

bool prev(false);
char rem(' ');
auto iter = std::remove_if(str.begin(), str.end(), [&] (char c) -> bool {
    if (c == rem && prev) {
        return true;
    }
    prev = (c == rem);
    return false;
});
in.erase(iter, in.end());

EDIT percebeu que std :: remove_if retorna um iterador que pode ser usado. código desnecessário removido.

5
paul23

Uma variante da resposta de Benjamin Lindley que usa uma expressão lambda para tornar as coisas mais limpas:

std::string::iterator new_end = 
        std::unique(str.begin(), str.end(),
        [=](char lhs, char rhs){ return (lhs == rhs) && (lhs == ' '); }
        );
str.erase(new_end, str.end());
4
tlaxcala

Por que não usar uma expressão regular:

boost::regex_replace(str, boost::regex("[' ']{2,}"), " ");

1
Mike Jiang

que tal isspace(lhs) && isspace(rhs) para lidar com todos os tipos de espaço em branco

0
Patrick Neary