4

So i'd like to read numbers from a .txt file as integers.

file.txt:

hello 123-abc
world 456-def

current code:

int number;
ifstream file("file.txt");
while (!file.eof())
{
    file >> number; //123, 456
}

Now this obviously doesnt work and ive been trying to solve this for a "while" but i just can not get around this.

iWillBeMaster
  • 137
  • 10
  • So you want to ignore anything that might not be a number? Try reading into a string. Then use a regular expresssion to match the string, and grab the number from one of the regular expression's matched groups. Confused yet? :-) The regex is admittedly simpler than other kinds of reading because of the consecutive series of characters like `123`, although `stoi` will generally pull off as many integers from the front of the string as they can so YMMV – AndyG May 10 '17 at 23:15
  • 1
    while(!file.eof()) doesnt do what u think it does – pm100 May 10 '17 at 23:20

3 Answers3

1

There are a number of ways you could do this. The method you've tried doesn't work because there is no number-like thing sitting at the read position in the stream. So the input will fail, and the stream's fail-bit will be set. You'll loop forever because you're only testing for eof. Read this for more information.

A simple way is to read a line at a time, and search for the first number by exploiting the second argument to std::strtol:

#include <iostream>
#include <string>
#include <experimental/optional>

std::experimental::optional<int> find_int_strtol( const std::string & s )
{
    for( const char *p = s.c_str(); *p != '\0'; p++ )
    {
        char *next;
        int value = std::strtol( p, &next, 10 );
        if( next != p ) {
            return value;
        }
    }
    return {};
}

int main()
{
    for( std::string line; std::getline( std::cin, line ); )
    {
        auto n = find_int_strtol( line );
        if( n )
        {
            std::cout << "Got " << n.value() << " in " << line << std::endl;
        }
    }
    return 0;
}

This is a bit clunky and it will also detect negatives, which you might not want. But it's a simple approach. The next pointer will be different to p if any characters were extracted. Otherwise the function fails. Then you increment p by 1 and search again. It looks like a polynomial search, but it's linear.

I've used std::optional from C++17, but I was testing on a C++14 compiler. It's for convenience. You could write the function without it.

Live example is here.

A more flexible way to solve problems like this is to use a regular expression. In this case, a simple numeric regex search is all you need. The following will only find positive integers, but you can use this type of pattern to find complex data too. Don't forget to include the header <regex>:

std::experimental::optional<int> find_int_regex( const std::string & s )
{
    static const std::regex r( "(\\d+)" );
    std::smatch match;
    if( std::regex_search( s.begin(), s.end(), match, r ) )
    {
        return std::stoi( match[1] );
    }
    return {};
}

Live example is here.

Community
  • 1
  • 1
paddy
  • 52,396
  • 6
  • 51
  • 93
0

You need to check if the file is open, then get the current line, then parse that current line to get the first number:

std::string currentLine = "";
std::string numbers = "";
ifstream file("file.txt");
if(file.is_open())
{
    while(std::getline(file, currentLine))
    {
        int index = currentLine.find_first_of(' '); // look for the first space
        numbers = currentLine.substr(index + 1, xyz);
    }
} 

xyz is the length of numbers ( 3 in this case if always constant ) or you can look for the next blank space by getting a substring from (index, currentLine.back() - index);

I'm sure you can figure out the rest, good luck.

Omar Martinez
  • 666
  • 6
  • 18
  • How do you know that the number will always have a space appearing before it? – paddy May 10 '17 at 23:36
  • I don't i assume it's a standard in this example since he shows two identical lines, but the concept is the same as long as there is a space in the file before and after the number. Get a substring, see if it can be cast to an int, if not then try the next substr, and so on. – Omar Martinez May 10 '17 at 23:44
0

Read line by line and remove all chars that are not digits. Finish with a std::stoi before pushing to your std::vector.

std::ifstream file{"file.txt"};
std::vector<int> numbers;

for (std::string s; std::getline(file, s);) {
    s.erase(std::remove_if(std::begin(s), std::end(s),
        [] (char c) { return !::isdigit(c); }), std::end(s));
    numbers.push_back(std::stoi(s));
}

Alternatively use std::regex_replace to remove non-digit chars:

auto tmp = std::regex_replace(s, std::regex{R"raw([^\d]+(\d+).+)raw"}, "$1");
numbers.push_back(std::stoi(tmp));

Live example

Snps
  • 13,516
  • 6
  • 49
  • 72