resueltos - ¿Cómo puedo verificar si una<cadena> de C++ comienza con una determinada cadena y convertir una subcadena en una int?
funciones de cadenas de caracteres en c++ (19)
¿Cómo hago lo siguiente (pseudocódigo de Python) en C ++?
if argv[1].startswith(''--foo=''):
foo_value = int(argv[1][len(''--foo=''):])
(Por ejemplo, si argv [1] es ''--foo = 98'', entonces foo_value es 98).
Actualización: dudo en investigar Boost, ya que solo estoy viendo un pequeño cambio en una simple herramienta de línea de comandos. (Prefiero no tener que aprender a enlazar y usar Boost para un cambio menor).
¿Por qué no usar gnu getopts? Aquí hay 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
Conseguirás
33
A riesgo de ser despedido por usar constructos C, creo que este ejemplo de sscanf
es más elegante que la mayoría de las soluciones de Boost. ¡Y no tiene que preocuparse por el enlace si 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 principal / final tan correctamente como el código Python equivalente, y más correctamente que cualquier cosa usando atoi
(que erróneamente ignorará 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
Aunque llevo 8 años tarde en la fiesta, esta es la manera más simple y divertida 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!
}
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());
}
Como C ++ 11 también se puede usar std::regex_search , por ejemplo, de la siguiente manera (devuelve una cadena vacía en caso de error):
#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();
}
Con C ++ 17 puede usar 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 administración de la memoria - es que solo contiene un puntero a una "cadena" (secuencia contigua de objetos tipo char) y conoce su tamaño. Ejemplo sin mover / copiar las cadenas fuente 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();
}
}
Dado que ambas cadenas - argv[1]
y "--foo"
- son cadenas C, la respuesta de @ FelixDombek es la mejor solución.
Al ver las otras respuestas, sin embargo, pensé que valía la pena señalar que, si su texto ya está disponible como std::string
, entonces existe una solución simple, cero copias, de 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());
Lo harías así:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = atoi(arg.substr(prefix.size()).c_str());
Buscar una lib como Boost.ProgramOptions que hace esto por ti también es una buena idea.
Nadie usó la función algorithm/mismatch STL todavía. Si esto devuelve verdadero, prefijo es un prefijo de ''toCheck'':
std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()
Prog. Completo de ejemplo:
#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())
Prog. Completo de ejemplo:
#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;
}
}
Ok, ¿por qué el complicado uso de las bibliotecas y esas cosas? Los objetos de cadena C ++ sobrecargan el operador [], por lo que puede simplemente comparar caracteres. Como lo que acabo de hacer, porque quiero enumerar todos los archivos en un directorio e ignorar los archivos invisibles y los ... 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..
Para completar, mencionaré la forma C de hacerlo:
Si
str
es su cadena original,substr
es la subcadena que desea verificar, luego
strncmp(str, substr, strlen(substr))
devolverá
0
sistr
comienza consubstr
. Las funcionesstrncmp
ystrlen
están en el archivo de cabecera C<string.h>
(publicado originalmente por Yaseen Rauf here , agregado marcado)
Para una comparación insensible a mayúsculas y minúsculas, utilice strnicmp
lugar de strncmp
.
Esta es la forma C de hacerlo, para cadenas de C ++ puede usar la misma función como esta:
strncmp(str.c_str(), substr.c_str(), substr.size())
Si está utilizando Boost, puede hacerlo con los algoritmos de la cadena de impulso + impulsar el elenco 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
}
Como la mayoría de las bibliotecas de boost, el algoritmo de cadena y el léxico son solo de encabezado, no hay nada para vincular.
Este tipo de enfoque, como muchas de las otras respuestas proporcionadas aquí, está bien para tareas muy simples, pero a la larga generalmente es mejor usar una biblioteca de análisis de línea de comando. Boost tiene uno ( Boost.Program_options ), que puede tener sentido si ya está utilizando Boost.
De lo contrario, una búsqueda de "analizador de líneas de comandos de C ++" arrojará una cantidad de opciones.
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''.
Usando STL esto podría verse así:
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;
}
Yo uso 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;
}
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 está probado. El principio es el mismo que el de Python. Requiere Boost.StringAlgo y Boost.LexicalCast.
Verifique si la cadena comienza con la otra cadena, y luego obtenga la subcadena (''slice'') de la primera cadena y conviértala usando yeso léxico.
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
// s starts with prefix
}
¿Quién necesita algo más? ¡STL puro!
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;
}
text.substr(0, start.length()) == start