How to read integers elegantly using C++ stream?

2020-03-01 18:03发布

问题:

I have a file full of lines in this format:

1 - 2: 3

I want to only load numbers using C++ streams. Whats the most elegant way to do it? I only thought about cin.get() and checikng each char if it is number or not.

回答1:

You can use a locale to change what things are read from the file as it is being read. That is, you will filter out all non-numeric values:

struct numeric_only: std::ctype<char> 
{
    numeric_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);

        std::fill(&rc['0'], &rc[':'], std::ctype_base::digit);
        return &rc[0];
    }
};

std::fstream myFile("foo.txt");
myfile.imbue(std::locale(std::locale(), new numeric_only()));

Then when you read your file, it'll convert all non digits to spaces while leaving you only the numbers. After that, you can simply use your normal conversions to transform what is being read into ints.

std::vector<int> intFromFile;
std::istream_iterator<int> myFileIter(myFile);
std::istream_iterator<int> eos;
std::copy(myFileIter, eos, std::back_inserter(intFromFile));

Response to the comments below:

Here is what I did to get it to work

int main(int args, char** argv){
    std::fstream blah;
    blah.open("foo.txt", std::fstream::in);
    if(!blah.is_open()){
        std::cout << "no file";
        return 0;
    }
    blah.imbue(std::locale(std::locale(), new numeric_only()));

    std::vector<int> intFromFile;
    std::istream_iterator<int> myFileIter(blah);
    std::istream_iterator<int> eos;
    std::copy(myFileIter, eos, std::back_inserter(intFromFile));

   return 0;
}

And this put only the ints into the vector, nothing more, nothing less. The reason it wasn't working before was two fold:

  1. I was filling up to '9' but not '9' itself. I've changed the fill to ':'
  2. Numbers larger than what an int can hold are a problem. I'd suggest using longs.


回答2:

I think this one would be the fastest -yet elegant- way:

int a, b, c;
scanf("%d-%d:%d", &a, &b, &c);


回答3:

I would recommend doing at least cursory sanity checks when reading this:

int a, b, c;
char dash, colon;

if (not (cin >> a >> dash >> b >> colon >> c) or dash != '-' or colon != ':')
    Failure. Do something.


回答4:

Sorry Konrad, but I recommend: never on pain of death, never never never (is that clear enough? :-) read formatted data from a file. Just don't.

There is only one correct way to do input of formatted data: read chunks of characters (typically lines but you can also read fixed length blocks).

Then parse the input text. You're not going to do a cursory check, you going to use a parser that guarantees to catch any formatting error, and report that error in a comprehensible way, take appropriate action (termination, skip the line and continue, whatever).

Separate input (the I/O operation) from parsing.

This advice from decades of experience as a commerical programmer: reading formatted input is for micky mouse proof-of-principal programs. Even if you have exclusive control of the creation of the file, always parse and check and report errors: after all, stuff changes, it may work today but not tomorrow.

I've been writing C++ for decades and I've never read an integer.



回答5:

Simply,

ifstream file("file.txt");
int n1, n2, n3;
char tmp;
while (file.good()) {
  file >> n1 >> tmp >> n2 >> tmp >> n3;
}


回答6:

    int a,b,c;

    cin >> a;
    cin.ignore(100,'-');
    cin >> b;
    cin.ignore(100,':');
    cin >> c;

    cout << "a = "<< a <<endl;
    cout << "b = "<< b <<endl;
    cout << "c = "<< c <<endl;

Input:

1 - 2: 3

Output:

a = 1
b = 2
c = 3

See yourself here : http://www.ideone.com/DT9KJ

Note: this can handle extra spaces also. So you can read even this:

 1     -        2      :      3

Similar topic:

Using ifstream as fscanf



标签: c++ input stream