Text File Parsing

Hello all,

As I was running my code, I ran into a certain problem. My function for searching for a range of scores or prices doesn't work. I've spent a few hours thinking about it and realized I could store all of the wine scores and prices into the class from the text file. However, A beginner like me doesn't know how to do this...:( So, how can I parse the file?

Here is the file:

Stags Leap Artemis Cabernet;Red;2013;92;65;Stags
Silver Oak Cabernet;Red;2011;91;110;Silver Oak
Joseph Phelps Insignia;Red;2013;97;240;Joseph
Duckhorn Cabernet;Red;2013;93;72;Duckhorn
Alpha Omega Chardonnay;White;2012;92;69.99;Alpha Omega
Grgich Chardonnay;White;2013;90;43;Grgich
Stags Leap Chardonnay;White;2014;90;30;Stags
Pahlmeyer;White;2013;93;72.99;Pahlmeyer


The format for the files is
wineName ; wineType; wineVintage; wineScore; winePrice; wineryName


Here's my code:
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;

class Winery
{
    public:
        Winery();
        Winery(string inputName, string inputStreet, string inputCity, 
               string inputState, string inputCountry);
        string getName();
        void getAddress();
    
    private:
        string wineryName;
        string street, city, state, country;
};

Winery::Winery()
{
    wineryName = "noname";
    street = "the";
    city = "middle";
    state = "of";
    country = "nowhere";
}

Winery::Winery(string inputName, string inputStreet, string inputCity, 
               string inputState, string inputCountry)
{
    inputName = wineryName;
    inputStreet = street;
    inputCity = city;
    inputState = state;
    inputCountry = country;
}

string Winery::getName()
{ return wineryName; }
void Winery::getAddress()
{ cout << street << ", " << city << ", " << state << ", " << country << " "; }

class Wine
{
    public:
        Wine();
        void setWineName(string inputName);
        string getName();
        void setWineVintage(int inputVintage);
        int getVintage();
        string getType();
        int getScore();
        double getPrice();
        void getWineryAddress();
        void print();
    
    private:
        string name;
        string type;
        int vintage;
        int score;
        double price;
        Winery * wineryOrigin;
};

Wine::Wine()
{
    name = "noname";
    type = "notype";
    vintage = 0;
    score = 0;
    price = 0;
}

void Wine::setWineName(string inputName)
{ name = inputName; }
string Wine::getName()
{ return name; }

void Wine::setWineVintage(int inputVintage)
{ vintage = inputVintage; }
int Wine::getVintage()
{ return vintage; }

string Wine::getType()
{ return type; }

int Wine::getScore()
{ return score; }

double Wine::getPrice()
{ return price; }

void Wine::getWineryAddress()
{
    cout << wineryOrigin->getName();
}

void findWines (string search)
{
    string line, lineArray[30];
    ifstream inFile;
    bool found = false;
    int results = 0;
    
    inFile.open("winelist.txt");
    
    size_t pos;
    while(inFile.good())
    {
        getline (inFile,line);
        pos = line.find (search);
        
        if (pos != string::npos)
        {
            lineArray[results] = line;
            results++;
            found = true;
        }
    }
    
    inFile.close();
    
    if (!found)
        cout << search << " not found. " << endl;
    
    else if (found)
    {
        cout << results << " result(s) of " << search << " found. " << endl;
        
        for (int j = 0; j < results; j++)
        {
            cout << "\nResult #" << j + 1 << ": " << endl;
            cout << lineArray [j] << endl;
        }
        cout << endl;
    }
}

void findRange (int wine[2])
{
    bool found = false;
    string line, lineArray[30];
    ifstream inFile;
    int result = 0, wineRange = wine[0];
    
    inFile.open ("winelist.txt");
    
    size_t pos;
    
    while(inFile.good())
    {
        while (wineRange <= wine[1])
        {
            getline (inFile,line);
            pos = line.find (wineRange);
            
            if (pos != string::npos)
            {
                lineArray[result] = line;
                result++;
                found = true;
            }
        }
    }
    
    inFile.close();
    
    if (!found)
        cout << "The range of " << wine[0] << " - " << wine[1] << " not found. " << endl;
    
    else if (found)
    {
        cout << result << " result(s) of the range of from " << wine[0] 
             << " - " << wine[1] << "found. " << endl;
        
        for (int j = 0; j < result; j++)
        {
            cout << "\nResult #" << j + 1 << ": " << endl;
            cout << lineArray [j] << endl;
        }
        cout << endl;
    }
}

int validateInput (int& input)
{
    while ((input != 1 && input != 2 && input != 3 && input != 4 && input != 5) || cin.fail())
    {
        cout << "Invalid input. " << endl;
        cout << "Would you like to: " << endl;
        cout << "1. Search for a range of wine prices " << endl
             << "2. Search for a range of wine scores " << endl
             << "3. Search for a wine vintage (year) " << endl
             << "4. Search for a wine type " << endl;
        cin >> input;
    }
    
    return input;
}

bool validateString (string word)
{
    for (char c : word)
    {
        if (!isalpha(c))
            return false;
    }
    
    return true;
}

int main()
{
    vector <Wine *> wineList;
    ifstream inStream;
    ofstream outStream;
    string line, type, vintage;
    int wine[2], input;
    
    while (input != 5)
    {
        cout << "Would you like to: " << endl;
        cout << "1. Search for a range of wine prices " << endl
             << "2. Search for a range of wine scores " << endl
             << "3. Search for a wine vintage (year) " << endl
             << "4. Search for a wine type " << endl
             << "5. Exit the program " << endl;
        cin >> input;
        
        validateInput (input);
        
        if (input == 1)
        {
            cout << "What is the range of prices you would like to search for?"
                 << "\n(enter first number and then second number eg 60-100 is 60 100)" << endl;
            cin >> wine[0] >> wine[1];
            
            while (cin.fail() || wine[0] <= 0 || wine[1] <= 0)
            {
                cin.clear();
                cin.ignore(1000, '\n');
                cout << "Invalid range. ";
                cout << "What is the range of prices you would like to search for?"
                     << "\n(enter first number and then second number eg 60-100 is 60 100)" << endl;
                cin >> wine[0] >> wine[1];
            }
            
            findRange (wine);
        }
        
        else if (input == 2)
        {
            cout << "What is the range of scores you would like to search for? " << endl;
            cin >> wine[0] >> wine[1];
            
            while (cin.fail() || wine[0] <= 0 || wine[1] <= 0)
            {
                cin.clear();
                cin.ignore(1000, '\n');
                cout << "Invalid range. ";
                cout << "What is the range of prices you would like to " 
                     << "search for?"
                     << "\n(enter first number, space, then second number." 
                     << "eg 60-100 is 60 100)" << endl;
                cin >> wine[0] >> wine[1];
            }
            
            findRange (wine);
        }
        
        else if (input == 3)
        {
            cout << "What would vintage would you like to search for? " << endl;
            cin >> vintage;
            
            findWines(vintage);
        }
        
        else if (input == 4)
        {
            cout << "What wine type would you like to search for? " << endl;
            cin >> type;
            
            while (!validateString(type))
            {
                cout << "Wine type can not contain numbers or special characters."
                     << Try again. " << endl;
                cout << "What wine type would you like to search for? " << endl;
                cin >> type;
            }
            findWines (type);
        }
        
        else if (input == 5)
            continue;
    }
    
    cout << "Bye! " << endl;
} 


Thanks,
VX
closed account (48T7M4Gy)
A start using string functions

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
#include <iostream>
#include <string>

int main()
{
    //SETUP THE LINES FROM THE FILE
    const int limit = 8;
    std::string line[limit] = {
        "Stags Leap Artemis Cabernet;Red;2013;92;65;Stags",
        "Silver Oak Cabernet;Red;2011;91;110;Silver Oak",
        "Joseph Phelps Insignia;Red;2013;97;240;Joseph",
        "Duckhorn Cabernet;Red;2013;93;72;Duckhorn",
        "Alpha Omega Chardonnay;White;2012;92;69.99;Alpha Omega",
        "Grgich Chardonnay;White;2013;90;43;Grgich",
        "Stags Leap Chardonnay;White;2014;90;30;Stags",
        "Pahlmeyer;White;2013;93;72.99;Pahlmeyer"
    };
    
    std::string  wineName, wineType, wineVintage, wineScore, winePrice, wineryName;
    
    size_t pos = 0, nextPos = 0;
    
    // RUN THROUGH EACH LINE AND EXTRACT DATA
    for(int i = 0; i < limit; i++)
    {
        pos = line[i].find_first_of(';');
        wineName = line[i].substr(0, pos);
        std::cout << "Wine name: " << wineName << '\n';
        
        nextPos = line[i].find(';');
        wineType = line[i].substr(nextPos + 1);
        std::cout << "Wine type: " << wineType << "\n\n";
        
        // etc etc
        // Or better still develop into a while loop or other automaton
        
    }
    
    return 0;
}
Wine name: Stags Leap Artemis Cabernet
Wine type: Red;2013;92;65;Stags

Wine name: Silver Oak Cabernet
Wine type: Red;2011;91;110;Silver Oak

Wine name: Joseph Phelps Insignia
Wine type: Red;2013;97;240;Joseph

Wine name: Duckhorn Cabernet
Wine type: Red;2013;93;72;Duckhorn

Wine name: Alpha Omega Chardonnay
Wine type: White;2012;92;69.99;Alpha Omega

Wine name: Grgich Chardonnay
Wine type: White;2013;90;43;Grgich

Wine name: Stags Leap Chardonnay
Wine type: White;2014;90;30;Stags

Wine name: Pahlmeyer
Wine type: White;2013;93;72.99;Pahlmeyer

Program ended with exit code: 0
realized I could store all of the wine scores and prices into the class from the text file.


... indeed you can, I am showing you how to do it for a struct and you can modify it for a class with your corresponding setters setWineName(), setWineVintage() etc.

Also, if you are allowed, I'd suggest storing the wines in a std::vector (instead of the C-style array you're using currrently) and you'll find searching, sorting for wines by name, vinery, type, score etc becomes much easier using the vector class methods and/or standard library algorithms:

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
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>

struct Wines
{
    std::string wineName;
    std::string wineType;
    int wineVintage;
    double wineScore;
    double winePrice;
    std::string wineWinery;

    Wines (const std::string& name, const std::string& type, const int& vintage,
           const double& score, const double& price, const std::string& winery)
   : wineName(name), wineType(type), wineVintage(vintage),
        wineScore(score), winePrice(price), wineWinery (winery){}
};
std::ostream& operator << (std::ostream& os, const Wines& w)
{
    os << w.wineName << " " << w.wineType << " " << w.wineVintage << " " <<
        w.wineScore << " " << w.winePrice << " " << w.wineWinery << '\n';

        return os;
}
int main()
{
    std::fstream inFile("D:\\input.txt");
    std::vector<Wines> wineList;

    while(inFile)
    {
        std::string  item;
        std::string name; std::string type; int vintage; double score; double price; std::string winery;
        getline (inFile, name, ';') &&  getline (inFile, type, ';') && getline (inFile, item, ';');
        std::stringstream stream_v (item);
        stream_v >> vintage;
        getline (inFile, item, ';');
        std::stringstream stream_s(item);
        stream_s >> score;
        getline (inFile, item, ';');
        std::stringstream stream_p(item);
        stream_p >> price && getline (inFile, winery);

        Wines w(name, type, vintage, score, price, winery);

            if (inFile)
            {
                wineList.push_back(std::move(w));
            }
    }
        for (auto& elem: wineList)
    {
        std::cout << elem ;
    }
}

Output
1
2
3
4
5
6
7
8
9
10
11
Stags Leap Artemis Cabernet Red 2013 92 65 Stags
Silver Oak Cabernet Red 2011 91 110 Silver Oak
Joseph Phelps Insignia Red 2013 97 240 Joseph
Duckhorn Cabernet Red 2013 93 72 Duckhorn
Alpha Omega Chardonnay White 2012 92 69.99 Alpha Omega
Grgich Chardonnay White 2013 90 43 Grgich
Stags Leap Chardonnay White 2014 90 30 Stags
Pahlmeyer White 2013 93 72.99 Pahlmeyer

Process returned 0 (0x0)   execution time : 0.047 s
Press any key to continue.
Last edited on
Using std::getline() with a delimiter and std::ws to discard leading white space:
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
struct wine
{
    wine() = default ;
    wine( std::string name, std::string type, int vintage,
          int score, double price, std::string winery_name )
              : name(name), type(type), vintage(vintage),
                score(score), price(price), winery_name(winery_name) {}

    std::string name = "no name" ;
    std::string type = "unknown" ;
    int vintage = 0 ;
    int score = 0 ;
    double price = 0 ;
    std::string winery_name = "unknown";
};

wine from_string( std::string str, char delimiter = ';' )
{
    std::istringstream stm(str) ;

    std::string name ;
    std::string type ;
    int vintage ;
    int score ;
    double price ;
    std::string winery_name ;

    char delim ;
    // http://en.cppreference.com/w/cpp/string/basic_string/getline
    // http://en.cppreference.com/w/cpp/io/manip/ws
    if( std::getline( stm, name, delimiter ) &&
        std::getline( stm, type, delimiter ) &&
        stm >> std::ws >> vintage >> delim && delim == delimiter &&
        stm >> score >> delim && delim == delimiter &&
        stm >> price >> delim >> std::ws && delim == delimiter &&
        std::getline( stm, winery_name, delimiter ) )
    {
        // http://www.stroustrup.com/C++11FAQ.html#uniform-init
        return { name, type, vintage, score, price, winery_name } ;
    }

    else return {} ; // parse failed
}

std::vector<wine> get_wines( std::istream& stm, char delimiter = ';' )
{
    std::vector<wine> result ;

    std::string line ; // error handling elided for brevity
    while( std::getline( stm, line ) ) result.push_back( from_string( line, delimiter ) ) ;

    return result ;
}

http://coliru.stacked-crooked.com/a/d53ea5f66e3994c0

Using the regular expressions library to parse the line:
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
wine from_string( std::string str, std::string delimiter = ";" ) // may throw
{
    std::regex delim(delimiter) ;
    // http://en.cppreference.com/w/cpp/regex/regex_token_iterator
    std::vector<std::string> tokens { std::sregex_token_iterator( str.begin(), str.end(), delim, -1 ),
                                      std::sregex_token_iterator{} } ;

    // http://en.cppreference.com/w/cpp/string/basic_string/stol
    // http://en.cppreference.com/w/cpp/string/basic_string/stof
    if( tokens.size() == 6 )
       return { tokens[0], tokens[1], std::stoi(tokens[2]), std::stoi(tokens[3]), std::stod(tokens[4]), tokens[5] } ;

    else return {} ; // parse failed
}

std::vector<wine> get_wines( std::istream& stm, std::string delimiter = ";" )
{
    std::vector<wine> result ;

    std::string line ;
    while( std::getline( stm, line ) )
    {
        try { result.push_back( from_string( line, delimiter ) ) ; }
        catch( const std::exception& ) { stm.clear(std::ios::failbit) ; }
    }

    return result ;
}

http://coliru.stacked-crooked.com/a/7b6536cfd9d34bdb
Wow, thanks! Inspired by both of your inputs, I made my own program which stores the information in a vector as you two suggested:

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <iostream>
using std::cout;
using std::endl;
using std::cin;

#include <fstream>
using std::ifstream;
using std::istringstream;

#include <cstring>
using std::string;

#include <vector>
using std::vector;

const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = ";";

class Winery
{
    public:
        Winery();
        Winery(string inputName, string inputStreet, string inputCity, string inputState, string inputCountry);
        string getName();
        void getAddress();
    
    private:
        string wineryName;
        string street, city, state, country;
};

Winery::Winery()
{
    wineryName = "noname";
    street = "the";
    city = "middle";
    state = "of";
    country = "nowhere";
}

Winery::Winery(string inputName, string inputStreet, string inputCity, string inputState, string inputCountry)
{
    inputName = wineryName;
    inputStreet = street;
    inputCity = city;
    inputState = state;
    inputCountry = country;
}

string Winery::getName()
{ return wineryName; }
void Winery::getAddress()
{ cout << street << ", " << city << ", " << state << ", " << country << " "; }

class Wine
{
public:
    Wine();
    void setWineName(string inputName);
    string getName();
    
    void setWineVintage(string inputVintage);
    string getVintage();
    
    void setWineType(string inputType);
    string getType();
    
    void setWineScore(string inputScore);
    string getScore();
    
    void setWinePrice(string inputPrice);
    string getPrice();
    
    void setWineBrand(string inputBrand);
    string getBrand();
    
    void getWineryAddress();
    void print();
    
    private:
        string name;
        string type;
        string vintage;
        string score;
        string price;
        string brand;
        Winery * wineryOrigin;
};

Wine::Wine()
{
    name = "noname";
    type = "notype";
    vintage = "0";
    score = "0";
    price = "0";
}

void Wine::setWineName(string inputName)
{ name = inputName; }
string Wine::getName()
{ return name; }

void Wine::setWineVintage(string inputVintage)
{ vintage = inputVintage; }
string Wine::getVintage()
{ return vintage; }

void Wine::setWineType(string inputType)
{ type = inputType; }
string Wine::getType()
{ return type; }

void Wine::setWineScore(string inputScore)
{ score = inputScore; }
string Wine::getScore()
{ return score; }

void Wine::setWinePrice(string inputPrice)
{ price = inputPrice; }
string Wine::getPrice()
{ return price; }

void Wine::setWineBrand(string inputBrand)
{ brand = inputBrand; }
string Wine::getBrand()
{ return brand; }

void Wine::getWineryAddress()
{
    cout << wineryOrigin->getName();
}

int main()
{
    ifstream fin;
    fin.open("winelist copy.txt");
    vector<Wine> wine(8);
    int i = 0;
    
    while (!fin.eof())
    {
        char buf[MAX_CHARS_PER_LINE];
        fin.getline(buf, MAX_CHARS_PER_LINE);
        
        int n = 0;
        
        const char* token[MAX_TOKENS_PER_LINE] = {};
        
        token[0] = strtok(buf, DELIMITER);
        if (token[0])
        {
            for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
            {
                token[n] = strtok(0, DELIMITER);
                if (!token[n])
                    break;
            }
        }
        
        //Setting all the values
        wine[i].setWineName(token[0]);
        wine[i].setWineType(token[1]);
        wine[i].setWineVintage(token[2]);
        wine[i].setWineScore(token[3]);
        wine[i].setWinePrice(token[4]);
        wine[i].setWineBrand(token[5]);
        
        i++;
    }
    
    /*This should be displaying all of the text file; I was just using this to see if my code
        was correctly storing the information. 
    */
    for (int j = 0; j < i; j++)
        cout << wine[j].getName() << " " << wine[j].getType() << " " << wine[j].getVintage()
               << " " << wine[j].getScore() << " " << wine[j].getPrice() << " " 
               << wine[j].getBrand() << endl;
}


Just running that though, it won't even work. My console simply displays
(lldb)

And then leads me to huge body of code:
641
642
643
644
645
646
647
648
649
650
651
652
653
654
...
static inline int compare(const char_type* __s1, const char_type* __s2, size_t __n)
        {return __n == 0 ? 0 : memcmp(__s1, __s2, __n);}
    static inline size_t length(const char_type* __s) {return strlen(__s);}
    static inline const char_type* find(const char_type* __s, size_t __n, const char_type& __a)
        {return __n == 0 ? NULL : (const char_type*) memchr(__s, to_int_type(__a), __n);}
    static inline char_type* move(char_type* __s1, const char_type* __s2, size_t __n)
        {return __n == 0 ? __s1 : (char_type*) memmove(__s1, __s2, __n);}
    static inline char_type* copy(char_type* __s1, const char_type* __s2, size_t __n)
        {
            _LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
            return __n == 0 ? __s1 : (char_type*)memcpy(__s1, __s2, __n);
        }
...


What is this? How can I fix this?

Thanks,
VX
Last edited on
closed account (48T7M4Gy)
1
2
3
4
while (!fin.eof()) // <-- DOOMED TO FAIL :(
    {
        char buf[MAX_CHARS_PER_LINE];
        fin.getline(buf, MAX_CHARS_PER_LINE);
Ohh...thanks @kemort. What can I replace !fin.eof() with?

Thanks,
VX
Nevermind, I have replaced while (!fin.eof()) with while(getline(fin, line)). Now, however, I am not getting all of what I whould be getting. It should display

Stags Leap Artemis Cabernet Red 2013 92 65 Stags
Silver Oak Cabernet Red 2011 91 110 Silver Oak
Joseph Phelps Insignia Red 2013 97 240 Joseph
Duckhorn Cabernet Red 2013 93 72 Duckhorn
Alpha Omega Chardonnay White 2012 92 69.99 Alpha Omega
Grgich Chardonnay White 2013 90 43 Grgich
Stags Leap Chardonnay White 2014 90 30 Stags
Pahlmeyer White 2013 93 72.99 Pahlmeyer


but this is all I am getting:


Silver Oak Cabernet Red 2011 91 110 Silver Oak
Duckhorn Cabernet Red 2013 93 72 Duckhorn
Grgich Chardonnay White 2013 90 43 Grgich
Pahlmeyer White 2013 93 72.99 Pahlmeyer


Here is my code:
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <iostream>
using std::cout;
using std::endl;
using std::cin;

#include <fstream>
using std::ifstream;
using std::istringstream;

#include <cstring>
using std::string;

#include <vector>
using std::vector;

const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = ";";

class Winery
{
    public:
        Winery();
        Winery(string inputName, string inputStreet, string inputCity, string inputState, string inputCountry);
        string getName();
        void getAddress();
    
    private:
        string wineryName;
        string street, city, state, country;
};

Winery::Winery()
{
    wineryName = "noname";
    street = "the";
    city = "middle";
    state = "of";
    country = "nowhere";
}

Winery::Winery(string inputName, string inputStreet, string inputCity, string inputState, string inputCountry)
{
    inputName = wineryName;
    inputStreet = street;
    inputCity = city;
    inputState = state;
    inputCountry = country;
}

string Winery::getName()
{ return wineryName; }
void Winery::getAddress()
{ cout << street << ", " << city << ", " << state << ", " << country << " "; }

class Wine
{
public:
    Wine();
    void setWineName(string inputName);
    string getName();
    
    void setWineVintage(string inputVintage);
    string getVintage();
    
    void setWineType(string inputType);
    string getType();
    
    void setWineScore(string inputScore);
    string getScore();
    
    void setWinePrice(string inputPrice);
    string getPrice();
    
    void setWineBrand(string inputBrand);
    string getBrand();
    
    void getWineryAddress();
    void print();
    
    private:
        string name;
        string type;
        string vintage;
        string score;
        string price;
        string brand;
        Winery * wineryOrigin;
};

Wine::Wine()
{
    name = "noname";
    type = "notype";
    vintage = "0";
    score = "0";
    price = "0";
}

void Wine::setWineName(string inputName)
{ name = inputName; }
string Wine::getName()
{ return name; }

void Wine::setWineVintage(string inputVintage)
{ vintage = inputVintage; }
string Wine::getVintage()
{ return vintage; }

void Wine::setWineType(string inputType)
{ type = inputType; }
string Wine::getType()
{ return type; }

void Wine::setWineScore(string inputScore)
{ score = inputScore; }
string Wine::getScore()
{ return score; }

void Wine::setWinePrice(string inputPrice)
{ price = inputPrice; }
string Wine::getPrice()
{ return price; }

void Wine::setWineBrand(string inputBrand)
{ brand = inputBrand; }
string Wine::getBrand()
{ return brand; }

void Wine::getWineryAddress()
{
    //cout << street << ", " << city << ", " << state << ", " << country << " ";
    cout << wineryOrigin->getName();
}

int main()
{
    ifstream fin;
    fin.open("winelist copy.txt");
    vector<Wine> wine(8);
    string line;
    int i = 0;
    
    while (getline(fin, line))
    {
        char buf[MAX_CHARS_PER_LINE];
        fin.getline(buf, MAX_CHARS_PER_LINE);
        
        int n = 0;
        
        const char* token[MAX_TOKENS_PER_LINE] = {};
        
        token[0] = strtok(buf, DELIMITER);
        if (token[0])
        {
            for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
            {
                token[n] = strtok(0, DELIMITER);
                if (!token[n])
                    break;
            }
        }
        
        wine[i].setWineName(token[0]);
        wine[i].setWineType(token[1]);
        wine[i].setWineVintage(token[2]);
        wine[i].setWineScore(token[3]);
        wine[i].setWinePrice(token[4]);
        wine[i].setWineBrand(token[5]);
        
        i++;
    }
    
    for (int j = 0; j < i; j++)
        cout << wine[j].getName() << " " << wine[j].getType() << " " << wine[j].getVintage()
               << " " << wine[j].getScore() << " " << wine[j].getPrice() << " " 
               << wine[j].getBrand() << endl;
}


How can I get it to display the full text file?

***Update***
It seems that the program is only parsing every other line. Weird. Even weirder, after placing
cout << i << endl;
after the while loop, i is 4. It should be 8.

Thanks again,
VX
Last edited on
Of course your only processing every other line since you read one line and throw away the string, then you read a line into C-string and try to parse that C-string instead of parsing the first string and avoiding the C-string altogether.

1
2
3
4
5
   while (getline(fin, line))  // Read a line into a string here.
    {
        char buf[MAX_CHARS_PER_LINE];
        fin.getline(buf, MAX_CHARS_PER_LINE); // Now read another line here.
Ahh, much thanks. Have changed it now:

143
144
145
...
for (int j = 0; j < 8; j++)
...


And now compiles perfectly!

Thanks so much,
VX
Last edited on
closed account (48T7M4Gy)
@VX Good to see things are moving forward. Now, try using while instead of for(int etc) because your file eventually would have more than 8 wines I suspect, and 8 as a limitation doesn't really do justice to using <vector>'s
In my opinion, it's a step backwards to move from a solution based on
 
    while (getline(fin, line))

to one based on a fixed number of iterations
 
    for (int j = 0; j < 8; j++)


The former has the advantage of being driven by the contents of the data file, while the latter requires the programmer to customise the program and rebuild it any time the contents of the file changes.

A possible solution might be to replace this:
144
145
146
147
    while (getline(fin, line))
    {
        char buf[MAX_CHARS_PER_LINE];
        fin.getline(buf, MAX_CHARS_PER_LINE);
with this:
144
145
146
    char buf[MAX_CHARS_PER_LINE];
    while (fin.getline(buf, MAX_CHARS_PER_LINE))
    {  

There is a related problem in the use of a vector of a predetermined size
I suggest replacing these lines
140
141
142
    vector<Wine> wine(8);
    string line;
    int i = 0;

with simply
 
    vector<Wine> wine;


replace these lines:
164
165
166
167
168
169
170
171
        wine[i].setWineName(token[0]);
        wine[i].setWineType(token[1]);
        wine[i].setWineVintage(token[2]);
        wine[i].setWineScore(token[3]);
        wine[i].setWinePrice(token[4]);
        wine[i].setWineBrand(token[5]);
        
        i++;
with these:
164
165
166
167
168
169
170
171
172
173
        Wine w;
        
        w.setWineName(token[0]);
        w.setWineType(token[1]);
        w.setWineVintage(token[2]);
        w.setWineScore(token[3]);
        w.setWinePrice(token[4]);
        w.setWineBrand(token[5]);
        
        wine.push_back(w);


and finally, replace:
 
    for (int j = 0; j < i; j++)
with
 
    for (unsigned int j = 0; j < wine.size(); j++)

Ah, thanks for the suggestions @Chervil and @kemort. I am unfamiliar with vectors and appreciate the help you are giving me. I will post updated code later.

Thanks,
VX
closed account (48T7M4Gy)
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

class Wine
{
private:
    std::string name;
    int year;
    
public:
    
    Wine (std::string aName = "??? UNKNOWN ???", int aYear = 666)
    {
        name = aName;
        year = aYear;
    }
    
    void setName( std::string aName ) { name = aName;}
    void setYear( int aYear ) { year = aYear;}
    
    std::string getName() { return name; }
    int getYear() { return year; }
};

bool compare_name( Wine lhs, Wine rhs) { return lhs.getName() < rhs.getName(); }
bool compare_year( Wine lhs, Wine rhs) { return lhs.getYear() < rhs.getYear(); }
size_t tokenize( std::string &, std::string &, std::string );

int main ()
{
    std::string line;
    std::string token;
    std::ifstream myfile ("tokenize_file.txt");
    
    const int no_properties = 6;
    std::string array[no_properties];
    int count = 0;
    
    std::vector<Wine> wine_list;
    
    Wine temp, rough_red("Rough red", 2017), temp_default;
    wine_list.push_back(rough_red);
    wine_list.push_back(temp_default); // JUST FOR DEFAULT CHECK
    
    if (myfile.is_open())
    {
        while( getline(myfile, line) and line.length() > 0 )
        {
            count = 0;
            std::cout << "LINE: " << line << '\n';
            while( tokenize(token, line, ";") != -1 )
            {
                array[count] = token;
                count++;
            }
            array[count] = line; // LAST ITEM LEFT IN LINE
            
            for(auto i: array)
                std::cout << i << '\n';
            std::cout << '\n';
            
            temp.setName(array[0]);
            temp.setYear(std::stoi(array[2]));
            
            wine_list.push_back(temp);
        }
        std::cout << "DATA READ COMPLETE\n\n";
        
        std::sort(wine_list.begin(), wine_list.end(), compare_name ); // SORT FOR LUCK - ON NAME
        std::cout << "WINE LIST SORTED ON NAME:\n";
        for(auto i: wine_list)
            std::cout << '*' << i.getName() << '-' << i.getYear() << '\n';
        
        std::cout << "\nWINE LIST SORTED ON YEAR:\n";
        std::sort(wine_list.begin(), wine_list.end(), compare_year ); // SORT FOR LUCK - ON YEAR
        for(auto i: wine_list)
            std::cout << '*' << i.getName() << '-' << i.getYear() << "\n\n";
        
        myfile.close();
    }
    else
        std::cout << "Unable to open file\n";
    
    std::cout << "Just for interest std::string::npos = " << std::string::npos << "\n\n";
    
    std::cout << "END\n";
    
    return 0;
}

size_t tokenize(std::string &token, std::string &aLine, std::string delimiter)
{
    size_t pos = aLine.find_first_of(delimiter);
    if (pos != std::string::npos) // THE 'THOMAS AMENDMENT'
    {
        token = aLine.substr(0, pos);
        aLine = aLine.erase(0, pos + 1);
    }
    return pos;
}
Last edited on
@kemort
1
2
size_t pos = aLine.find_first_of(delimiter);
if (pos != -1)


size_t is unsigned so it never can be -1.
string::find_first_of returns string::npos if it doesn't find it.
closed account (48T7M4Gy)
@Thomas

http://www.cplusplus.com/reference/string/string/npos/
std::string::npos

static const size_t npos = -1;

Maximum value for size_t
npos is a static member constant value with the greatest possible value for an element of type size_t.

This value, when used as the value for a len (or sublen) parameter in string's member functions, means "until the end of the string".

As a return value, it is usually used to indicate no matches.

This constant is defined with a value of -1, which because size_t is an unsigned integral type, it is the largest possible representable value for this type.


I think I get away with what I had, especially in view of a to and fro we had with lastchance recently, but I think it is better to use the clearer and special string::npos as you mention. I'll check my copy and make the necessary changes :)

Which I have now done.
Last edited on
Hello all,

Sorry for taking so long to respond. My computer got a virus and it took a while to get rid of it. Anyways, I have modified the code @kemort provided into something that fitted the instructions of my assignment. Here is the code:

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

class Wine
{
private:
    string name;
    int year, score;
    double price;
    
public:
    
    Wine (string aName = "??? UNKNOWN ???", int aYear = 666, int aScore = 0, double aPrice = 0)
    {
        name = aName;
        year = aYear;
        score = aScore;
        price = aPrice;
    }
    
    void setName (string aName)
    { name = aName;}
    string getName()
    { return name; }
    
    void setYear (int aYear)
    { year = aYear;}
    int getYear()
    { return year; }

    void setScore (int aScore)
    { score = aScore; }
    int getScore ()
    { return score; }

    void setPrice (double aPrice)
    { price = aPrice; }
    double getPrice ()
    { return price; }
};

bool compare_Score (Wine i, Wine j)
{ return i.getScore () < j.getScore (); }
bool compare_Price (Wine lhs, Wine rhs)
{ return lhs.getPrice () < rhs.getPrice (); }
size_t tokenize(string &, string &, string);

void findWines (string search)
{
    string line, lineArray[30];
    ifstream inFile;
    bool found = false;
    int results = 0;
    
    inFile.open("winelist copy.txt");
    
    size_t pos;
    while(inFile.good())
    {
        getline (inFile,line);
        pos = line.find (search);
        
        if (pos != string::npos)
        {
            lineArray[results] = line;
            results++;
            found = true;
        }
    }
    
    inFile.close();
    
    if (!found)
        cout << search << " not found. " << endl;
    
    else if (found)
    {
        cout << results << " result(s) of " << search << " found. " << endl;
        
        for (int j = 0; j < results; j++)
        {
            cout << "\nResult #" << j + 1 << ": " << endl;
            cout << lineArray [j] << endl;
        }
        cout << endl;
    }
}

int main ()
{
    string line, token, wineType;
    ifstream myfile ("winelist copy.txt");
    
    const int no_properties = 6;
    string array[no_properties];
    int count = 0, input;
    
    vector<Wine> wine_list;
    
    Wine temp, rough_red("Rough red", 2017), temp_default;
    wine_list.push_back(rough_red);
    wine_list.push_back(temp_default);
    
    while (input != 6)
    {
        while( getline(myfile, line) and line.length() > 0 )
        {
            count = 0;
            
            while( tokenize(token, line, ";") != -1 )
            {
                array[count] = token;
                count++;
            }
            array[count] = line;
            temp.setName(array[0]);
            temp.setYear(stoi(array[2]));
            
            wine_list.push_back(temp);
        }
        
        cout << "Would you like to: "
             << "\n1. Search for a range of wine scores "
             << "\n2. Search for a range of wine prices "
             << "\n3. Display all wines sorted by score "
             << "\n4. Display all wines sorted by price "
             << "\n5. Search for a wine type "
             << "\n6. End Program" << endl;
        cin >> input;
        
        if (input == 1)
        {
            //Ahhh! Spent so long trying to do this >:(
            ;
        }
        
        else if ( input == 2)
        {
            //Same problem here >:(
            ;
        }
        
        else if (input == 3)
        {
            sort(wine_list.begin(), wine_list.end(), compare_Score);
            cout << "WINE LIST SORTED ON SCORE:\n";
            for(auto i: wine_list)
                cout << "* " << i.getName() << '-' << i.getScore() << '\n';
        }
        
        else if (input == 4)
        {
            cout << "\nWINE LIST SORTED ON PRICE:\n";
            sort (wine_list.begin(), wine_list.end(), compare_Price);
            for (auto i: wine_list)
                cout << "* " << i.getName() << '-' << i.getPrice() << endl;
        }
        
        else if (input == 5)
        {
            cout << "What wine would you like to look for? " << endl;
            cin >> wineType;
            
            findWines(wineType);
        }
    }
    myfile.close();
    cout << "PROGRAM END\n";
    
    return 0;
}

size_t tokenize(string &token, string &aLine, string delimiter)
{
    size_t pos = aLine.find_first_of(delimiter);
    if (pos != string::npos)
    {
        token = aLine.substr(0, pos);
        aLine = aLine.erase(0, pos + 1);
    }
    return pos;
}


I am having trouble with trying to search for a range of wine prices and scores. Also, when I try to either display wines sorted by score or price, this is what I get:

WINE LIST SORTED ON SCORE
* Rough red-0
* ??? UNKNOWN ???-0
* Stags Leap Artemis Cabernet-0
* Silver Oak Cabernet-0
* Joseph Phelps Insignia-0
* Duckhorn Cabernet-0
* Alpha Omega Chardonnay-0
* Grgich Chardonnay-0
* Stags Leap Chardonnay-0
* Pahlmeyer-0


After the dash, there should be the score. I am having the same problem with the sorting by price.

I would appreciate any help any of you coding maestros could provide me ;)

Thanks,
VX
closed account (48T7M4Gy)
I think you need to show us your attempts.

You should be able to write the required functions and isolate where the problems are with substantial detail.
check to see that score is populated with the values you expect. Sorting off a field that is all zeros isn't going to do anything useful.


Topic archived. No new replies allowed.