Averigüe si la cadena termina con otra cadena en C ++


270

¿Cómo puedo saber si una cadena termina con otra cadena en C ++?

Respuestas:


211

Simplemente compare los últimos n caracteres usando std::string::compare:

#include <iostream>

bool hasEnding (std::string const &fullString, std::string const &ending) {
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

int main () {
    std::string test1 = "binary";
    std::string test2 = "unary";
    std::string test3 = "tertiary";
    std::string test4 = "ry";
    std::string ending = "nary";

    std::cout << hasEnding (test1, ending) << std::endl;
    std::cout << hasEnding (test2, ending) << std::endl;
    std::cout << hasEnding (test3, ending) << std::endl;
    std::cout << hasEnding (test4, ending) << std::endl;

    return 0;
}

Sí, esta es la mejor manera de hacerlo, sin duda.
Noldorin

3
Siempre odio calcular los índices de las subcadenas, es muy propenso a la separación ... Prefiero iterar hacia atrás desde el final de ambas cadenas, tratando de encontrar una falta de coincidencia.
xtofl

17
@Noldorin No estoy de acuerdo. Es obvio: la mejor manera de hacerlo es usar una biblioteca. Es una pena que la biblioteca estándar de C ++ haga tan pocas cosas útiles.
masterxilo

1
@masterxilo ¿Qué biblioteca propone para resolver este problema y cómo es esa biblioteca una mejor opción que una función (básicamente) de una línea?
Brandin

33
@Brandin Porque es una funcionalidad tan básica. C ++ nos obliga a reprogramar una y otra vez las mismas funcionalidades que se proporcionan de fábrica en cualquier otro lenguaje informático moderno. El hecho de que las personas necesiten ir a stackoverflow para resolver esta pregunta muestra que hay un pb.
Conchylicultor

175

Utiliza esta función:

inline bool ends_with(std::string const & value, std::string const & ending)
{
    if (ending.size() > value.size()) return false;
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}

3
Tenga en cuenta que a MSVC10 no le gusta esta solución: std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()en modo de depuración, arroja:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu

154

Uso boost::algorithm::ends_with(ver, por ejemplo, http://www.boost.org/doc/libs/1_34_0/doc/html/boost/algorithm/ends_with.html ):

#include <boost/algorithm/string/predicate.hpp>

// works with const char* 
assert(boost::algorithm::ends_with("mystring", "ing"));

// also works with std::string
std::string haystack("mystring");
std::string needle("ing");
assert(boost::algorithm::ends_with(haystack, needle));

std::string haystack2("ng");
assert(! boost::algorithm::ends_with(haystack2, needle));

83

Tenga en cuenta que a partir de c ++ 20 std :: string finalmente se proporcionará begin_with y ends_with . Parece que existe la posibilidad de que con c ++ 30 cadenas en c ++ finalmente puedan ser utilizables, si no está leyendo esto desde un futuro lejano, puede usar estos comienzos con / fines con:

#include <string>

static bool endsWith(const std::string& str, const std::string& suffix)
{
    return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
}

static bool startsWith(const std::string& str, const std::string& prefix)
{
    return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
}

y algunas sobrecargas de ayuda adicionales:

static bool endsWith(const std::string& str, const char* suffix, unsigned suffixLen)
{
    return str.size() >= suffixLen && 0 == str.compare(str.size()-suffixLen, suffixLen, suffix, suffixLen);
}

static bool endsWith(const std::string& str, const char* suffix)
{
    return endsWith(str, suffix, std::string::traits_type::length(suffix));
}

static bool startsWith(const std::string& str, const char* prefix, unsigned prefixLen)
{
    return str.size() >= prefixLen && 0 == str.compare(0, prefixLen, prefix, prefixLen);
}

static bool startsWith(const std::string& str, const char* prefix)
{
    return startsWith(str, prefix, std::string::traits_type::length(prefix));
}

En mi opinión, las cadenas de c ++ son claramente disfuncionales, y no fueron hechas para usarse en el código del mundo real. Pero existe la esperanza de que esto mejore al menos.


2
Como str.compare no devuelve un valor booleano, no es tan inteligente probar "== 0" utilizando el operador not ("!"), Ya que puede ser confuso para los lectores. Utilice "... && str.compare (...) == 0" para mayor claridad.
Thomas Tempelmann

@Pavel ¿Hay alguna razón para no usar std :: string :: find en sus métodos "comienza con"?
Maxime Oudot

44
@MaximeOudot ¡Por supuesto que sí! ¿Por qué querría buscar una cadena completa si necesita saber si comienza con algo? En otras palabras, puede terminar buscando una cadena de 100mb de largo para encontrar la pieza al final y luego ignorar ese resultado porque no está al comienzo de la cadena.
Pavel P

1
Más "1" para la predicción de c ++ 30.
Inocente espectador

40

Sé que la pregunta es para C ++, pero si alguien necesita una buena función de C para hacer esto:


/*  returns 1 iff str ends with suffix  */
int str_ends_with(const char * str, const char * suffix) {

  if( str == NULL || suffix == NULL )
    return 0;

  size_t str_len = strlen(str);
  size_t suffix_len = strlen(suffix);

  if(suffix_len > str_len)
    return 0;

  return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len );
}


25

El std::mismatchmétodo puede cumplir este propósito cuando se utiliza para iterar hacia atrás desde el final de ambas cadenas:

const string sNoFruit = "ThisOneEndsOnNothingMuchFruitLike";
const string sOrange = "ThisOneEndsOnOrange";

const string sPattern = "Orange";

assert( mismatch( sPattern.rbegin(), sPattern.rend(), sNoFruit.rbegin() )
          .first != sPattern.rend() );

assert( mismatch( sPattern.rbegin(), sPattern.rend(), sOrange.rbegin() )
          .first == sPattern.rend() );

3
+1. Yo había std :: nunca se dio cuenta desajuste () antes - Me pregunto qué otra cosa está en que los algoritmos de archivo de cabecera que nunca he mirado ...
j_random_hacker

3
Creo que vale la pena una pregunta SO en sí misma: ¿alguna vez has navegado por las funciones stl disponibles?
xtofl

2
Tenga en cuenta que esto tiene el mismo requisito que std::equal: debe verificar de antemano que el supuesto sufijo no sea más largo que la cadena en la que lo está buscando. Si no se verifica eso, se genera un comportamiento indefinido.
Rob Kennedy

18

En mi opinión, la solución más simple de C ++ es:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}

10
¡Esto es bastante lento ya que buscará toda la cadena en slugar de solo probar el final!
Alexis Wilke

2
@nodakai, si tengo una cadena de 1Mb, será mucho más que nanosegundos.
Alexis Wilke

No lo creo ... tiene que hacer strlen en cualquier caso, y luego comienza a mirar desde el final.
LtWorf

2
@LtWorf std::string::size()es una operación de tiempo constante; lo que no necesita strlen.
Thomas

2
¿Cómo se puede considerar esta solución cuando falla para el caso cuando suffix.size () == s.size () + 1. Fragmento de código que muestra esto en líneagdb.com/S1ITVqKDL ? La complejidad es irrelevante si no funciona correctamente en todos los casos.
c0ntrol

10

Deje aser una cadena y bla cadena que busca. Use a.substrpara obtener los últimos n caracteres de ay compárelos con b (donde n es la longitud de b)

O usar std::equal(incluir <algorithm>)

Ex:

bool EndsWith(const string& a, const string& b) {
    if (b.size() > a.size()) return false;
    return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin());
}

¿Cómo puedo devolver verdadero también si termina después de mi cadena con \ r o \ n o ambos ??? ¡Gracias!
sofr

@Dario: Su solución usando std :: equal () es buena, la que usa substr () no tanto, a menos que esté usando cadenas COW (y creo que hay pocas personas), substr () implica crear una segunda copia de parte de la cadena, lo que implica la asignación dinámica de memoria. Esto puede fallar, y en cualquier caso significa que se usa más memoria que otras soluciones (y es casi seguro que es más lento que otras soluciones).
j_random_hacker

4

Permítanme extender la solución de Joseph con la versión que no distingue entre mayúsculas y minúsculas ( demostración en línea )

static bool EndsWithCaseInsensitive(const std::string& value, const std::string& ending) {
    if (ending.size() > value.size()) {
        return false;
    }
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin(),
        [](const char a, const char b) {
            return tolower(a) == tolower(b);
        }
    );
}

3

igual que el anterior, aquí está mi solución

 template<typename TString>
  inline bool starts_with(const TString& str, const TString& start) {
    if (start.size() > str.size()) return false;
    return str.compare(0, start.size(), start) == 0;
  }
  template<typename TString>
  inline bool ends_with(const TString& str, const TString& end) {
    if (end.size() > str.size()) return false;
    return std::equal(end.rbegin(), end.rend(), str.rbegin());
  }

1
¿ starts_withPor qué usa 'string :: compare'? ¿Por qué no std::equal(start.begin(), start.end(), str.begin())?
Dmytro Ovdiienko

Solo porque comienzos_con fue el primero que necesitaba. ends_with fue agregado más tarde.
dodjango

3

Otra opción es utilizar expresiones regulares. El siguiente código hace que la búsqueda sea insensible a mayúsculas / minúsculas:

bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
  return std::regex_search(str,
     std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}

Probablemente no tan eficiente, pero fácil de implementar.


Para cualquier persona con C ++ 11 o superior, esto es muy conveniente.
Clare Macrae

¡Cuidado, las expresiones regulares pueden ser increíblemente lentas en C ++!
mxmlnkn

regex para esto es como ... Necesito desestimar esto. No lo haré pero debería.
MK.

2

puedes usar string :: rfind

El ejemplo completo basado en comentarios:

bool EndsWith(string &str, string& key)
{
size_t keylen = key.length();
size_t strlen = str.length();

if(keylen =< strlen)
    return string::npos != str.rfind(key,strlen - keylen, keylen);
else return false;
}

3
-1. Sí, podría usarlo, pero es innecesariamente lento en el caso de que la cadena no termine con el final proporcionado; el escaneo continuará hasta el inicio de la cadena. Además, no menciona que necesita una prueba posterior para asegurarse de que el final coincida al final de la cadena , en lugar de en cualquier otro lugar de la cadena.
j_random_hacker

Acabo de poner el enlace de la función necesaria y creo que es muy fácil hacerlo desde la documentación str.rfind (key, str.length () - key.length (), key.length ());
Ahmed dijo

OK, eso es eficiente, pero en ese caso string :: find () funcionaría igual de bien. También debe mencionar el caso donde key.length ()> str.length (): el código que sugiere en su comentario se bloqueará en este caso. Si actualiza su respuesta con esta información, soltaré mi -1.
j_random_hacker

2

Compruebe si str tiene sufijo , utilizando a continuación:

/*
Check string is end with extension/suffix
*/
int strEndWith(char* str, const char* suffix)
{
  size_t strLen = strlen(str);
  size_t suffixLen = strlen(suffix);
  if (suffixLen <= strLen) {
    return strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0;
  }
  return 0;
}

2

Utilice el algoritmo std :: equal de <algorithms>con iteración inversa:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}

2
Si bien este código puede proporcionar una solución a la pregunta, es mejor agregar contexto sobre por qué / cómo funciona. Esto puede ayudar a los futuros usuarios a aprender y aplicar ese conocimiento a su propio código. También es probable que reciba comentarios positivos de los usuarios en forma de votos a favor, cuando se explique el código.
borchvm

@borchvm, agregó alguna explicación, espero que ayude a comprender
Sergei

1

Respecto a la respuesta de Grzegorz Bazior. Usé esta implementación, pero la original tiene un error (devuelve verdadero si comparo ".." con ".so"). Propongo una función modificada:

bool endsWith(const string& s, const string& suffix)
{
    return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}

1

Pensé que tenía sentido publicar una solución en bruto que no usa ninguna función de biblioteca ...

// Checks whether `str' ends with `suffix'
bool endsWith(const std::string& str, const std::string& suffix) {
    if (&suffix == &str) return true; // str and suffix are the same string
    if (suffix.length() > str.length()) return false;
    size_t delta = str.length() - suffix.length();
    for (size_t i = 0; i < suffix.length(); ++i) {
        if (suffix[i] != str[delta + i]) return false;
    }
    return true;
}

Agregando un simple std::tolowerpodemos hacer que este caso sea insensible

// Checks whether `str' ends with `suffix' ignoring case
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
    if (&suffix == &str) return true; // str and suffix are the same string
    if (suffix.length() > str.length()) return false;
    size_t delta = str.length() - suffix.length();
    for (size_t i = 0; i < suffix.length(); ++i) {
        if (std::tolower(suffix[i]) != std::tolower(str[delta + i])) return false;
    }
    return true;
}

gracias por agregar esto las soluciones ligeras siempre son geniales
ekkis

1

Encontré esta buena respuesta al problema similar "startWith":

¿Cómo verifico si un C ++ std :: string comienza con una cadena determinada y convierto una subcadena en un int?

Puede adoptar la solución para buscar solo en el último lugar de la cadena:

bool endsWith(const std::string& stack, const std::string& needle) {
    return stack.find(needle, stack.size() - needle.size()) != std::string::npos;
}

De esta manera, puede hacerlo corto, rápido, usar c ++ estándar y hacerlo legible.


0

Si eres como yo y no te gusta el purismo de C ++, aquí hay un viejo híbrido skool. Hay una ventaja cuando las cadenas son más que un puñado de caracteres, como la mayoríamemcmp implementaciones comparan palabras de máquina cuando es posible.

Debe tener el control del conjunto de caracteres. Por ejemplo, si este enfoque se usa con utf-8 o tipo wchar, hay alguna desventaja ya que no admitirá la asignación de caracteres, por ejemplo, cuando dos o más caracteres son lógicamente idénticos .

bool starts_with(std::string const & value, std::string const & prefix)
{
    size_t valueSize = value.size();
    size_t prefixSize = prefix.size();

    if (prefixSize > valueSize)
    {
        return false;
    }

    return memcmp(value.data(), prefix.data(), prefixSize) == 0;
}


bool ends_with(std::string const & value, std::string const & suffix)
{
    size_t valueSize = value.size();
    size_t suffixSize = suffix.size();

    if (suffixSize > valueSize)
    {
        return false;
    }

    const char * valuePtr = value.data() + valueSize - suffixSize;

    return memcmp(valuePtr, suffix.data(), suffixSize) == 0;
}

0

Mis dos centavos:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.