How to Read Text File Starting at Numbers

I am trying to read this text file into three separate vectors, one for temp,voltage, and sensitivity.
1
2
3
4
5
6
7
    Temp.      Voltage    Sensitivity
    (Kelvin)   (Volts)    (milliVolts/Kelvin)

       1.400   1.64429    -1.248933E+01
       1.500   1.64299    -1.362982E+01
       1.600   1.64157    -1.479334E+01
       1.700   1.64003    -1.597987E+01


I am able to successfully read the value into the vectors using this code, but only if I delete the words first. So my question is how to skip the words and start reading into the vectors at the numbers? Also I cannot simply specify which line to start at because the text file may change.
1
2
3
4
5
6
7
8
9
10
11
12
13
    void Convert::readFile()
    {
        inFile.open("DT-670.txt");
        if (inFile) {
            cout << "File Open";

            while(inFile>>tempKelvin>>tempmV>>tempSensitivity)
            {
                kelvin.push_back(tempKelvin);
                mV.push_back(tempmV);
                sensitivity.push_back(tempSensitivity);
            }
    }



the easy way is to do 2 getlines into a string and simply ignore the data, then read the values from there.
@jonnin so the problem with that is that the text file could change and I wouldnt know how many lines are words possibly. For example, another text file could have 3 lines of words then the numbers
It looks like you probably want to close the file after reading the values. It's best to declare inFile in the function. You also probably want to exit the program (or do something!) if the file doesn't open.

It looks like you may have declared some "temporary" variables inside your class. That's a bad idea. They should be in the function.

You also might want to use a vector of structs instead of three separate vectors. Actually, mV seems to be misnamed according to the file heading since Voltage is in volts, not milliVolts.

There might be an easier way to skip the unknown number of text lines, but here's one way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstdlib> // for exit

using namespace std;  // I assume you know this is frowned upon!

struct Record   // give this a more specific name
{
    double temperature;
    double voltage;
    double sensitivity;
};

istream& operator>>(istream& is, Record& rec)
{
    return is >> rec.temperature >> rec.voltage >> rec.sensitivity;
}

ostream& operator<<(ostream& os, const Record& rec)
{
    return os << rec.temperature << ", "
              << rec.voltage     << ", "
              << rec.sensitivity;
}

class Convert
{
    constexpr static const char* InputFile = "DT-670.txt";
    vector<Record> records;
public:
    void readFile();
    void printRecords();
};

void Convert::readFile()
{
    ifstream inFile(InputFile);
    if (!inFile)
    {
        cerr << "Cannot open " << InputFile << '\n';
        exit(EXIT_FAILURE);
    }

    // Find the first line that starts with a floating point value.
    string line;
    while (getline(inFile, line))
    {
        string::size_type pos = line.npos;
        // Find first position in line that is not a space or tab.
        if ((pos = line.find_first_not_of(" \t")) != line.npos)
        {
            char c = line[pos];
            // If it starts with '.', '-', or '+' then increment the position.
            if (c == '.' || c == '-' || c == '+')
                if (++pos >= line.size())
                    continue; // can't be a number; read the next line

            // If it's the beginning of a number, break this loop.
            if (c >= '0' && c <= '9')
                break;
        }
    }

    // Now read the numbers.
    do
    {
        istringstream iss(line);
        Record temp;
        iss >> temp;
        records.push_back(temp);
    }
    while (getline(inFile, line));
}

void Convert::printRecords()
{
    for (const auto& rec: records)
        cout << rec << '\n';
}

int main()
{
    Convert conv;
    conv.readFile();
    conv.printRecords();
}


EDIT: It occurred to me that a "text" line might be all dashes, so assuming a line whose first non-whitespace character is '-' is not sufficient. Fixed that above.
Last edited on
Here's a simplification. Replace Convert::readFile above with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Convert::readFile()
{
    ifstream inFile(InputFile);
    if (!inFile)
    {
        cerr << "Cannot open " << InputFile << '\n';
        exit(EXIT_FAILURE);
    }

    string line;
    while (getline(inFile, line))    // read a line at a time
    {
        istringstream iss(line);
        Record temp;
        if (iss >> temp)    // if you can read three floats from this line, then push the record
            records.push_back(temp);
    }
}

Last edited on
constexpr static const char* InputFile = "DT-670.txt";

Dutch why have you used 'const' and 'static'?

'const' shouldn't matter because 'constexpr' already makes sure that the value isn't changed.. right?
'static'.. why? The pointer is a private variable and only the member functions can use it right? And the member functions should be able to access it without the static..?

Is it because "DT-670.txt" is of that type or something? I'm clueless..

@Grime, If you remove any one of "constexpr", "static", or "const", then "gcc -std=C++14 -Wall -Wextra -pedantic" complains.

'const' shouldn't matter because 'constexpr' already makes sure that the value isn't changed.. right?

constexpr says that the variable's value can be evaluated at compile time. And although it implies const, the const it implies applies only to the variable itself (the pointer in this case), not what the pointer points to. Since what the pointer points to is a string literal, it technically needs the const.

'static'.. why? The pointer is a private variable and only the member functions can use it right? And the member functions should be able to access it without the static..?

static ensures that there is only one InputFile variable even if more than one object is created. Also, you aren't allowed to use constexpr here if it's not also static.

If it had been this
 
    const char* InputFile = "DT-670.txt";

or even this
 
    const char* const InputFile = "DT-670.txt";

then each object would get it's own "InputFile" variable.

However, now that I think about it, it might make more sense to just make it a char* (or std::string) and pass in the filename when the object is constructed.
Last edited on
@dutch Thank you for the help! As you can probably tell I am somewhat new to this. I originally used a vector instead of a struct because i wanted to use a binary search to sort through the tmep or voltage and find its corresponding value. How can I do that using a struct?
@dutch Also I am trying to convert the voltage into mV and have done that using a vector but dont know how to convert just the voltage while reading into struct using your method
You can convert to millivolts in the input function:

1
2
3
4
5
6
istream& operator>>(istream& is, Record& rec)
{
    is >> rec.temperature >> rec.voltage >> rec.sensitivity;
    rec.voltage *= 1000.0;
    return is;
}

It's unclear what you mean by "sort through" and find the "corresponding value". But I can guess. :-) I assume the "corresponding value" you want is the sensitivity. Exactly how you would do that depends on the relationship between temperature and voltage.

From the given table it looks like:
(A) as the temperature increases the voltage decreases, and
(B) the input table is already sorted (by increasing temperature).
Are both A and B always the case?

If A is always true but B is not always true, it's easy enough to sort the table after reading it in. Then you can do a binary search on either the temperature (increasing) or voltage (decreasing) to find the corresponding sensitivity.

If A is not always true, then it's a little more difficult since if the table is sorted by temperature then it wouldn't be sorted (even in reverse order) for the voltage, and vice versa.
Last edited on
Topic archived. No new replies allowed.