tags:

views:

380

answers:

6

I'm trying to read just the integers from a text file structured like this....

ALS 46000
BZK 39850
CAR 38000
//....

using ifstream.

I've considered 2 options.

1) Regex using Boost

2) Creating a throwaway string ( i.e. I read in a word, don't do anything with it, then read in the score ). However, this is a last resort.

Are there any ways to express in C++ that I want the ifstream to only read in text that is an integer? I'm reluctant to use regular expressions if it turns out that there is a much simpler way to accomplish this.

+1  A: 

You can call ignore to have in skip over a specified number of characters.

istr.ignore(4);

You can also tell it to stop at a delimiter. You would still need to know the maximum number of characters the leading string could be, but this would also work for shorter leading strings:

istr.ignore(10, ' ');

You could also write a loop that just reads characters until you see the first digit character:

char c;
while (istr.getchar(c) && !isdigit(c))
{
    // do nothing
}
if (istr && isdigit(c))
    istr.putback(c);
R Samuel Klatchko
I'm not sure how this could be implemented if I was reading these from a stream. For example, while ( scoreFile >> foo ) would read in the ALS only. Then the next time through the loop it would read in the int only. How could ignore help in this case.
Anonymous
You call them alternately. `while (istr) { istr.ignore(4); int num = 0; istr >> num; /* do something with num */ }`
R Samuel Klatchko
A: 

here goes :P

private static void readFile(String fileName) {

        try {
            HashMap<String, Integer> map = new HashMap<String, Integer>();
            File file = new File(fileName);

            Scanner scanner = new Scanner(file).useDelimiter(";");
            while (scanner.hasNext()) {
                String token = scanner.next();
                String[] split = token.split(":");
                if (split.length == 2) {
                    Integer count = map.get(split[0]);
                    map.put(split[0], count == null ? 1 : count + 1);
                    System.out.println(split[0] + ":" + split[1]);
                } else {
                    split = token.split("=");
                    if (split.length == 2) {
                        Integer count = map.get(split[0]);
                        map.put(split[0], count == null ? 1 : count + 1);
                        System.out.println(split[0] + ":" + split[1]);
                    }
                }
            }
            scanner.close();
            System.out.println("Counts:" + map);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        readFile("test.txt");
    }
}
Mike Redford
That appears to be Java. The OP was asking for C++.
R Samuel Klatchko
Which makes me love the simplicity of C++ so much more :-)
Martin York
+8  A: 

why to make simple things complicated?

whats wrong in this :

ifstream ss("C:\\test.txt");

int score;
string name;
while( !ss.eof() )
{
    ss >> name >> score ;
    // do something with score
}
Learn to use the formatting provided by SO.
Martin York
Unfortunately that is a typical anti-pattern. The EOF test (as implied by your while(ss) ) will not return true until 'AFTER' you read past the end of file. Thus the 'do something' will do something with invalid values when there is no input left. Prefer 'while ( ss >> name >> score)' So the loop is only entered if the read succeeds.
Martin York
A: 
fscanf(file, "%*s %d", &num);

or %05d if you have leading zeros and fixed width of 5....

sometimes the fastest way to do things in C++ is to use C. :)

Tim Lovell-Smith
Of course I forgot that you're using ifstream... but do you have to?
Tim Lovell-Smith
Prefer the simpler C++ way: file >> word >> number;
Martin York
A: 

You can create a ctype facet that classifies letters as white space. Create a locale that uses this facet, then imbue the stream with that locale. Having that, you can extract numbers from the stream, but all letters will be treated as white space (i.e. when you extract numbers, the letters will be ignored just like a space or a tab would be):

Such a locale can look like this:

#include <iostream>
#include <locale>
#include <vector>
#include <algorithm>

struct digits_only: std::ctype<char> 
{
    digits_only(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
        static std::vector<std::ctype_base::mask> 
            rc(std::ctype<char>::table_size,std::ctype_base::space);

        if (rc['0'] == std::ctype_base::space)
            std::fill_n(&rc['0'], 9, std::ctype_base::mask());
        return &rc[0];
    }
};

Sample code to use it could look like this:

int main() {
    std::cin.imbue(std::locale(std::locale(), new digits_only()));

    std::copy(std::istream_iterator<int>(std::cin), 
        std::istream_iterator<int>(),
        std::ostream_iterator<int>(std::cout, "\n"));
}

Using your sample data, the output I get from this looks like this:

46000
39850
38000

Note that as it stands, I've written this to accept only digits. If (for example) you were reading floating point numbers, you'd also want to retain '.' (or the locale-specific equivalent) as the decimal point. One way to handle things is to start with a copy of the normal ctype table, and then just set the things you want to ignore as space.

Jerry Coffin
+2  A: 
t.g.