简体   繁体   中英

How to read numbers separated with space from a file

I have a text file with numbers in it, the first number is some number written in different number systems, then there is a space, and then there is another number - which is a base the first number is written in. So for example:

1011011 2 
1212011 3 
1AB13 12 
etc...

I must convert all the numbers in the file to a decimal system using a function that I already have. It takes two variables, the first one is a number written in different numeric system, and the other one is the base of that system, and then it converts that number to decimal and writes it in another file.

How do I read the data from the file so the first number goes to the first variable and the base to the second variable?

You can read a number as a string, and then its base as a number, by std::ifstream::operator>> . Then you can do your conversion to base 10.

#include <fstream>
#include <iostream>

std::ifstream ifs("test.txt");
std::string num;
int base;
while (ifs >> num >> base)
{
    std::cout << num << " in base " << base << "\n";
}

There are many possible solutions. Some are better, some are not so good. It depends a little bit on your requirements.

In any case I do prefer a more object oriented approach in C++. So, we have data, like a string consisting of a number and a base. We want to split this somehow and extract a base. At the end we want to calculate a decimal number out of that.

So, we can define a small class, eg NumberAndBase. This class knows, how to read and write its data. And nobody else should know that. That can be somehow encapsulated. As a solution, we will overwrite the inserter and extractor operator of our tiny class.

We can then simple read and write the data like this:

    NumberAndBase nab{};
    std::cin >> nab;
    std::cout << nab << "\n\n";

This works of course with every iostream . std::cin and std::cout are just examples here.

But you want to read more data that are stored in a file. So you can do this by simply reading the data and put them in a std::vector . Like so:

std::vector<NumberAndBase> data{ std::istream_iterator<NumberAndBase>(sourceFile), std::istream_iterator<NumberAndBase>() };

This defines a variable data and uses its range constructor. For begin and end, we will use the std::istream_iterator . This will call the inserter operator of the class and read and convert all values.

Now I will show you a complete example. Please note. I do not have files on SO, so I use an std::istringstream instead, But you can of course exchange this with any std::ifstream , or whatever std::istream you want.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <vector>

unsigned long yourConversionFunction(std::string& s, unsigned int b) { 
    return std::stoul(s, 0, b); 
}

struct NumberAndBase {

    NumberAndBase() {}
    explicit NumberAndBase(std::string& s) : numberAsString(s), base(), result() { convert(s); }
    explicit NumberAndBase(std::string& s, unsigned int b) : numberAsString(s), base(b), result() { result = yourConversionFunction(s,b); }

    unsigned long convert(std::string& s) { 
        std::istringstream iss(s); iss >> numberAsString >> base;
        result = yourConversionFunction(numberAsString, base);
        return result;
     }

    // Extractor. Read number and base, separated by space
    friend std::istream& operator >> (std::istream& is, NumberAndBase& nab) {
        std::string line{};
        if (std::getline(is, line)) nab.convert(line);
        return is;
    }
    // Inserter: Show result
    friend std::ostream& operator << (std::ostream& os, const NumberAndBase& nab) {
        return os << std::left << std::setw(16) << nab.numberAsString << " (" << std::setw(2) << nab.base << ")  -->  " << nab.result;
    }

    // Data
    std::string numberAsString{};
    unsigned int base{ 10 };
    unsigned long result{ 0 };

};


std::istringstream sourceFile{ R"(10011010010    2
1200201        3
103102         4
14414          5
5414           6
3412           7
2322           8
1621           9
1234           10
a22            11
86a            12
73c            13
642            14
574            15
4d2            16
44a            17
3ea            18
37i            19
31e            20)" };

int main() {
    // Read all data
    std::vector<NumberAndBase> data{ std::istream_iterator<NumberAndBase>(sourceFile), std::istream_iterator<NumberAndBase>() };

    // Show result
    std::copy(data.begin(), data.end(), std::ostream_iterator<NumberAndBase>(std::cout, "\n"));

    return 0;
}

You could of course combine the 2 statements in main into one (if you just want to show the result)

int main() {
    std::copy(
        std::istream_iterator<NumberAndBase>(sourceFile),
        std::istream_iterator<NumberAndBase>(),
        std::ostream_iterator<NumberAndBase>(std::cout, "\n"));
    return 0;
}

As said. One of the many possible solutions. I addedd also some constructors and stuff (not needed in this example, just to give you an idea). This could be adapted to your needs.

Just to be complete: Result of program

10011010010      (2 )  -->  1234
1200201          (3 )  -->  1234
103102           (4 )  -->  1234
14414            (5 )  -->  1234
5414             (6 )  -->  1234
3412             (7 )  -->  1234
2322             (8 )  -->  1234
1621             (9 )  -->  1234
1234             (10)  -->  1234
a22              (11)  -->  1234
86a              (12)  -->  1234
73c              (13)  -->  1234
642              (14)  -->  1234
574              (15)  -->  1234
4d2              (16)  -->  1234
44a              (17)  -->  1234
3ea              (18)  -->  1234
37i              (19)  -->  1234
31e              (20)  -->  1234

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM