'Save integers from string to vector

I need to save integers from string to vector.

Definition of number: each of strings substrings which consists entirely of digits, with a space before the first character of that substring and a space or punctuation mark after the last character, unless the substring is at the very beginning or end of the string (in this case there does not have to be a space in front or space or punctuation behind).

EXAMPLE 1: "120 brave students, 35 failed, remaining 85..." Numbers: 120, 35, 85

EXAMPLE 2: "2PAC and U2 have class" Numbers: // there aren't any numbers

#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
int CharToInt(int c) {
  return abs('0' - c);
}
int MakeNumber(std::vector < int > temp) {
  int num;
  if (temp.size() == 1)
    num = temp[0];
  if (temp.size() == 2)
    num = temp[0] * 10 + temp[1];
  if (temp.size() == 3)
    num = temp[0] * 100 + temp[1] * 10 + temp[2];
  if (temp.size() == 4)
    num = temp[0] * 1000 + temp[1] * 100 + temp[2] * 10 + temp[3];
  return num;
}

std::vector < int > ExtractNumbers(std::string str) {
  std::vector < int > a;
  bool found_number;
  std::vector < int > temp;
  for (int i = 0; i < str.length(); i++) {
    // skip spaces
    found_number = false;
    while (str[i] == ' ' && i < str.length())
      i++;
    // inside word
    while (str[i] != ' ' && i < str.length()) {
      while (str[i] >= '0' && str[i] <= '9' && i < str.length()) {
        temp.push_back(CharToInt(str[i]));
        i++;
        found_number = true;
      }
      i++;
    }
    if (found_number)
      a.push_back(MakeNumber(temp));
    temp.clear();
  }
  return a;
}

int main() {
  std::vector < int > a = ExtractNumbers("120 brave students, 35 failed, remaining 85...");
  std::vector < int > b = ExtractNumbers("2PAC and U2 have class");
  for (int i: a) std::cout << i << " ";
  std::cout << std::endl;
  for (int i: b) std::cout << i << " ";
  return 0;
}

OUTPUT:

120 35 85 // ✅

2 2 // ❌

Could you help me to modify this to work correctly for the example 2? How could I check if there is space before and space/punctuation after the number is found?



Solution 1:[1]

I found a way to solve this.

#include <iostream>
#include <string>
#include <vector>
std::vector < int > extract_numbers(std::string s) {
  std::vector < int > vek;
  for (int i = 0; i < s.length(); i++) {
    std::string word;
    while (s[i] == ' ' && i < s.length()) i++;
    while (s[i] != ' ' && i < s.length()) word += s[i++];
    int j = 0;
    while (word[j] >= '0' && word[j] <= '9') j++;
    std::string spaces = ".,;!?";
    for (int i = 0; i < spaces.size(); i++)
      if (word[j] == spaces[i] || word[j] == '\0') {
        vek.push_back(std::stoi(word));
        break;
      }
  }
  return vek;
}
int main() {
  std::string s;
  std::getline(std::cin, s);
  for (int i: extract_numbers(s))
    std::cout << i << " ";
  return 0;
}

Solution 2:[2]

That makeNumber should be

int MakeNumber(std::vector <int> temp) {
    int num = 0;
    for( int digit : temp){
        num *= 10;
        num += digit;
    }
    return num;
}

doesnt change the answer but will work with any number of digits (assuming it fits in an int)

Solution 3:[3]

Your requirement, that digits may be trailed by punctuation, but not by other characters, so they are considered a number, makes the problem a little bit harder.

One way to tackle this "parsing rule" is to use regular expressions. First, we split the input string into words (surrounded by whitespace), then we test if the number requirement is fulfilled for a word.

Instead of falling back to nitty gritty C-style character based parsing code, we use std::istringstream to read one word at a time from the string.

Finally, whatever we consider a number, we convert with std::stoi().

#include <iostream>
#include <sstream>
#include <algorithm>
#include <string>
#include <vector>
#include <cctype>
#include <regex>

using StringVector = std::vector<std::string>;
using I32Vector = std::vector<int32_t>;

static std::regex number_regex(R"(\d+[\.\,\:\;\!\?]*)");

inline bool is_number(const std::string& s) {
  std::cmatch m;
  return std::regex_match(s.c_str(),m,number_regex);
}

I32Vector extract_numbers(const std::string& s) {
  I32Vector result;
  std::string word;
  std::istringstream is{s};
  while (!is.eof()) {
    is >> word;
    std::cout << "[" << word << "]";
    if (is_number(word)) {
      std::cout << " is a number.";
      std::cout.flush();
      result.push_back(std::stoi(word));
    }
    std::cout << std::endl;
  }
  return result;
}

std::ostream& operator<<(std::ostream& stream,
             const I32Vector& v) {
  if (v.empty())
    stream << "()" << std::endl;
  else {
    stream << "(" << v[0];
    for (size_t i = 1; i < v.size(); i++) {
      stream << " " << v[i];
    }
    stream << ")" << std::endl;
  }
  return stream;
}

int main (int argc, const char* argv[]) {
  StringVector test_strings{
    "120 brave students, 35 failed, remaining 85...",
    "2PAC and U2 have class",
    "120 brave students, 35 failed,2 remaining 85..."
  };

  for (const auto& s : test_strings) {
    I32Vector numbers = extract_numbers(s);
    std::cout << s << " => " << numbers << std::endl;
  }
  
  return 0;
}

which yields after compiling, e.g. with clang++-13 -std=c++20 -o numbers numbers.cpp:

120 brave students, 35 failed, remaining 85... => (120 35 85)
2PAC and U2 have class => ()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rocket Procd
Solution 2 pm100
Solution 3