C ++ Extrait le numéro du milieu d’une chaîne

J’ai un vector contenant des ssortingngs qui suivent le format text_number-number

Example_45-3 : Example_45-3

Je veux seulement le premier numéro ( 45 dans l’exemple) et rien d’autre que je puisse faire avec mon code actuel:

 std::vector imgNumStrVec; for(size_t i = 0; i < StrVec.size(); i++){ std::vector seglist; std::ssortingngstream ss(StrVec[i]); std::ssortingng seg, seg2; while(std::getline(ss, seg, '_')) seglist.push_back(seg); std::ssortingngstream ss2(seglist[1]); std::getline(ss2, seg2, '-'); imgNumStrVec.push_back(seg2); } 

Existe-t-il des moyens plus simples et plus simples de procéder? et si oui quels sont-ils?

Je demande purement par désir d’apprendre à mieux coder car au bout du compte, le code ci-dessus n’extrait avec succès que le premier chiffre, mais il semble long et arrondi.

Exemple d’implémentation minimale fonctionnant sur de nombreuses chaînes (pas uniquement les chaînes de la forme “text_45-text”:

 ssortingng s = boost::regex_replace( ssortingng("Example_45-3"), boost::regex("[^0-9]*([0-9]+).*"), ssortingng("\\1") ); 

Autres exemples de chaînes sur lesquelles cela fonctionnerait:

  • “asdfasdf 45 sdfsdf”
  • “X = 45, sdfsdf”

Pour cet exemple, j’ai utilisé g ++ sous Linux avec #include et -lboost_regex . Vous pouvez également utiliser une expression rationnelle C ++ 11x.

N’hésitez pas à éditer ma solution si vous avez une meilleure regex.


Commentaire:

S’il n’y a pas de contraintes de performances, utiliser Regex est idéal pour ce genre de chose car vous ne réinventez pas la roue (en écrivant un tas de code d’parsing de chaînes qui prend du temps à écrire / à tester de manière exhaustive).

De plus, si / quand vos chaînes deviennent plus complexes ou ont des modèles plus variés, regex s’adapte facilement à la complexité. (L’exemple de motif de la question est assez simple. Mais souvent, un motif plus complexe prend entre 10 et plus de 100 lignes de code lorsqu’une expression rationnelle à une ligne fait de même.)

Vous pouvez également utiliser les commandes find_first_of et find_first_not_of pour trouver le premier “numberssortingng” dans une chaîne.

 std::ssortingng first_numberssortingng(std::ssortingng const & str) { std::size_t const n = str.find_first_of("0123456789"); if (n != std::ssortingng::npos) { std::size_t const m = str.find_first_not_of("0123456789", n); return str.substr(n, m != std::ssortingng::npos ? mn : m); } return std::ssortingng(); } 

Cela devrait être plus efficace que la solution d’Ashot Khachatryan. Notez l’utilisation de '_' et '-' au lieu de "_" et "-" . Et aussi, la position de départ de la recherche de '-' .

 inline std::ssortingng mid_num_str(const std::ssortingng& s) { std::ssortingng::size_type p = s.find('_'); std::ssortingng::size_type pp = s.find('-', p + 2); return s.substr(p + 1, pp - p - 1); } 

Si vous avez besoin d’un nombre au lieu d’une chaîne, comme ce que la solution d’Alexandr Lapenkov a fait, essayez également les solutions suivantes:

 inline long mid_num(const std::ssortingng& s) { return std::strtol(&s[s.find('_') + 1], nullptr, 10); } 

Regarde ça

 std::ssortingng ex = "Example_45-3"; int num; sscanf( ex.c_str(), "%*[^_]_%d", &num ); 

Je peux penser à deux façons de le faire:

  • Utilisez des expressions régulières
  • Utilisez un iterator pour parcourir la chaîne et copiez chaque chiffre consécutif dans un tampon temporaire. Pause quand elle atteint une longueur déraisonnable ou sur le premier non-chiffre après une chaîne de chiffres consécutifs. Ensuite, vous avez une chaîne de chiffres que vous pouvez facilement convertir.
 std::ssortingng s = "Example_45-3"; int p1 = s.find("_"); int p2 = s.find("-"); std::ssortingng number = s.substr(p1 + 1, p2 - p1 - 1) 

La meilleure façon de le faire dans C ++ 11 et versions ultérieures est probablement d’utiliser des expressions régulières , qui combinent une expressivité élevée et des performances élevées lorsque le test est répété suffisamment souvent.

Le code suivant illustre les bases. Vous devez #include pour que cela fonctionne.

 // The example inputs std::vector inputs { "Example_0-0", "Example_0-1", "Example_0-2", "Example_0-3", "Example_0-4", "Example_1-0", "Example_1-1", "Example_1-2", "Example_1-3", "Example_1-4" }; // The regular expression. A lot of the cost is incurred when building the // std::regex object, but when it's reused a lot that cost is amortised. std::regex imgNumRegex { "^[^_]+_([[:digit:]]+)-([[:digit:]]+)$" }; for (const auto &input: inputs){ // This wil contain the match results. Parts of the regular expression // enclosed in parentheses will be stored here, so in this case: both numbers std::smatch matchResults; if (!std::regex_match(input, matchResults, imgNumRegex)) { // Handle failure to match abort(); } // Note that the first match is in str(1). str(0) contains the whole ssortingng std::ssortingng theFirstNumber = matchResults.str(1); std::ssortingng theSecondNumber = matchResults.str(2); std::cout << "The input had numbers " << theFirstNumber; std::cout << " and " << theSecondNumber << std::endl; } 

En utilisant la réponse de @ Pixelchemist et par exemple std::stoul :

 bool getFirstNumber(std::ssortingng const & a_str, unsigned long & a_outVal) { auto pos = a_str.find_first_of("0123456789"); try { if (std::ssortingng::npos != pos) { a_outVal = std::stoul(a_str.substr(pos)); return true; } } catch (...) { // handle conversion failure // ... } return false; }