¿Cómo puedo comprobar si un C++ comienza con una cadena determinada y convertir una subcadena en una int?


¿Cómo hago lo siguiente (pseudocódigo Python) en C++?

if argv[1].startswith('--foo='):
    foo_value = int(argv[1][len('--foo='):])

(Por ejemplo, si argv[1] es 'fo foo=98', entonces foo_value es 98.)

Actualización: No estoy seguro de mirar en Boost, ya que solo estoy mirando a hacer un cambio muy pequeño a una pequeña herramienta de línea de comandos simple. (Prefiero no tener que aprender a vincular y usar Boost para un cambio menor.)

Author: Daryl Spitzer, 2009-12-10

19 answers

Si estás usando Boost, puedes hacerlo con algoritmos de cadena boost + potencie el reparto léxico:

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

try {    
    if (boost::starts_with(argv[1], "--foo="))
        foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
    // bad parameter
}

Al igual que la mayoría de las bibliotecas de boost, el algoritmo de cadena y el reparto léxico son solo de encabezado, no hay nada para vincular.

Este tipo de enfoque, al igual que muchas de las otras respuestas proporcionadas aquí, está bien para tareas muy simples, pero a largo plazo generalmente es mejor usar una biblioteca de análisis de línea de comandos. Boost tiene uno (Boost.Program_options ), que puede hacer siente si ya estás usando Boost.

De lo contrario, una búsqueda de "analizador de línea de comandos de c++" producirá una serie de opciones.

 101
Author: Ferruccio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-25 22:23:44

Lo harías así:

   std::string prefix("--foo=");
   if (!arg.compare(0, prefix.size(), prefix))
      foo_value = atoi(arg.substr(prefix.size()).c_str());

Buscando una lib como Boost.ProgramOptions que hace esto por usted también es una buena idea.

 165
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-10 01:07:31

Solo para completar, mencionaré la forma C de hacerlo:

Si str es su cadena original, substr es la subcadena que desea control, entonces

strncmp(str, substr, strlen(substr))

Volverá 0 si str comienza con substr. Las funciones strncmp y strlen están en la C archivo de cabecera <string.h>

(publicado originalmente por Yaseen Rauf aquí , marcado añadido)

Para una comparación que no distingue entre mayúsculas y minúsculas, use strnicmp en lugar de strncmp.

Esta es la forma en C de hacerlo, para las cadenas de C++ puede usar la misma función de esta manera:

strncmp(str.c_str(), substr.c_str(), substr.size())
 115
Author: Felix Dombek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-27 10:53:57
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
  // s starts with prefix
}

¿Quién necesita algo más? ¡Pure STL!

 102
Author: user3046585,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-20 20:42:38

Código que uso yo mismo:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}
 75
Author: Hüseyin Yağlı,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-17 08:02:41

Nadie usó la función STL algorithm/mismatch todavía. Si esto devuelve true, prefix es un prefijo de 'toCheck':

std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()

Ejemplo completo prog:

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "Will print true if 'prefix' is a prefix of string" << std::endl;
        return -1;
    }
    std::string prefix(argv[1]);
    std::string toCheck(argv[2]);
    if (prefix.length() > toCheck.length()) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "'prefix' is longer than 'string'" <<  std::endl;
        return 2;
    }
    if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
        std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
        return 0;
    } else {
        std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
        return 1;
    }
}

Editar:

Como sugiere @James T. Huggett, std::equal es una mejor opción para la pregunta: ¿Es A un prefijo de B? y es un código ligeramente más corto:

std::equal(prefix.begin(), prefix.end(), toCheck.begin())

Ejemplo completo prog:

#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, char **argv) {
  if (argc != 3) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "Will print true if 'prefix' is a prefix of string"
              << std::endl;
    return -1;
  }
  std::string prefix(argv[1]);
  std::string toCheck(argv[2]);
  if (prefix.length() > toCheck.length()) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "'prefix' is longer than 'string'" << std::endl;
    return 2;
  }
  if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
    std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
              << '"' << std::endl;
    return 0;
  } else {
    std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
              << toCheck << '"' << std::endl;
    return 1;
  }
}
 38
Author: matiu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-21 15:21:56

Dado que ambas cadenas - argv[1] y "--foo" - son cadenas C, La respuesta de@FelixDombek es sin duda la mejor solución.

Al ver las otras respuestas, sin embargo, pensé que vale la pena señalar que, si su texto ya está disponible como std::string, entonces existe una solución simple, de copia cero y máxima eficiencia que no se ha mencionado hasta ahora:

const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(strlen(foo));

Y si foo ya es una cadena:

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());
 22
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:32

A riesgo de ser flameado por usar construcciones C, creo que este ejemplo sscanf es más elegante que la mayoría de las soluciones Boost. ¡Y no tiene que preocuparse por la vinculación si se está ejecutando en cualquier lugar que tenga un intérprete de Python!

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    for (int i = 1; i != argc; ++i) {
        int number = 0;
        int size = 0;
        sscanf(argv[i], "--foo=%d%n", &number, &size);
        if (size == strlen(argv[i])) {
            printf("number: %d\n", number);
        }
        else {
            printf("not-a-number\n");
        }
    }
    return 0;
}

Aquí hay un ejemplo de salida que demuestra que la solución maneja la basura inicial/final tan correctamente como el código Python equivalente, y más correctamente que cualquier cosa que use atoi (que ignorará erróneamente un sufijo no numérico).

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
 9
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-10 03:29:00

Usando STL esto podría verse como:

std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
  std::istringstream iss(arg.substr(prefix.size()));
  iss >> foo_value;
}
 7
Author: razvanco13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-09 09:42:09

¿Por qué no usar gnu getopts? He aquí un ejemplo básico (sin controles de seguridad):

#include <getopt.h>
#include <stdio.h>

int main(int argc, char** argv)
{
  option long_options[] = {
    {"foo", required_argument, 0, 0},
    {0,0,0,0}
  };

  getopt_long(argc, argv, "f:", long_options, 0);

  printf("%s\n", optarg);
}

Para el siguiente comando:

$ ./a.out --foo=33

Obtendrá

33
 6
Author: Carl Cook,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-11 12:08:06

Utilizo std::string::compare envuelto en el método de utilidad como a continuación:

static bool startsWith(const string& s, const string& prefix) {
    return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
 4
Author: ShitalShah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-22 10:15:00

También puedes usar strstr:

if (strstr(str, substr) == substr) {
    // 'str' starts with 'substr'
}

Pero creo que es bueno solo para cadenas cortas porque tiene que recorrer toda la cadena cuando la cadena no comienza realmente con 'substr'.

 3
Author: szx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-26 23:46:55

Ok ¿por qué el uso complicado de las bibliotecas y esas cosas? Los objetos de cadena de C++ sobrecargan el operador [], por lo que solo puede comparar caracteres.. Como lo que acabo de hacer, porque quiero enumerar todos los archivos en un directorio e ignorar los archivos invisibles y el.. y . pseudofiles.

    while ((ep = readdir(dp)))
    {
        string s(ep->d_name);
        if(!(s[0] == '.'))      // Omit invisible files and .. or .
            files.push_back(s);
    }

Es así de simple..

 2
Author: Nils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-15 11:56:01
std::string text = "--foo=98";
std::string start = "--foo=";

if (text.find(start) == 0)
{
    int n = stoi(text.substr(start.length()));
    std::cout << n << std::endl;
}
 1
Author: mois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-09 07:44:49
text.substr(0, start.length()) == start
 1
Author: Macsinus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-13 13:36:30

Con C++17 puede utilizar std::basic_string_view & con C++20 std::basic_string::starts_with o std::basic_string_view::starts_with.

El beneficio de std::string_view en comparación con std::string - con respecto a la gestión de memoria - es que solo tiene un puntero a una "cadena" (secuencia contigua de objetos similares a char) y conoce su tamaño. Ejemplo sin mover / copiar las cadenas de origen solo para obtener el valor entero:

#include <string_view>
#include <exception>
#include <iostream>

const char * argument = "--foo=42"; // Emulating command argument.
const char * argumentPrefix = "--foo";
int inputValue = 0;

std::string_view argView = argument;
if (argView.starts_with(argumentPrefix))
{
    std::string_view prefixView = argumentPrefix; // Helper for getting the size of argumentPrefix.
    try
    {
        // The underlying data of argView is nul-terminated, therefore we can use data().
        inputValue = std::atoi(argView.substr(prefixView.size() + 1).data());
    }
    catch (std::exception& e)
    {
        std::cerr << e.what();
    }
}
 1
Author: Roi Danton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-04 20:49:34

Aunque llego 8 años tarde a la fiesta, aquí está la forma más simple de hacerlo:

#include <iostream>
#include <string>

using namespace std; // for clarity's sake.

bool startswith(string prefix, string text) {
    if (prefix.length() > 0 && text.length() > prefix.length()) {
        int i = 0;
        while (i < prefix.length()) {
            if (text[i] != prefix[i]) return false;
            i++;
        }
        return true;
    }

    else return false;
}

int main (int argc, char* argv) {
    if(startswith("--foo=", argv[1]))
        // do something about it!
}
 0
Author: 7kemZmani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-27 11:48:46

Desde C++11 también std:: regex_search se puede usar, por ejemplo, de la siguiente manera (devuelve una cadena vacía si falla):

#include <regex>

std::string startsWith(const std::string &str, const std::string &prefix) {
  std::smatch match;
  std::regex_search(str, match, std::regex("^" + prefix));
  return match.suffix();
}
 0
Author: alextoind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 15:43:35
if(boost::starts_with(string_to_search, string_to_look_for))
    intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));

Esto no ha sido probado. El principio es el mismo que el de Python. Requiere Impulso.StringAlgo y Boost.LexicalCast.

Compruebe si la cadena comienza con la otra cadena, y luego obtenga la subcadena ('slice') de la primera cadena y conviértala usando un molde léxico.

 -3
Author: blwy10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-10 01:38:53