Trouble with find

The following code works to a certain extent, but with 2 exceptions. 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
using namespace std;

int main ()
        {

std::string search = "EoGUESS"; // search pattern
std::string line;
ifstream Myfile;
Myfile.open ("in1.txt");

if(Myfile.is_open())
        {
        bool isFound;
        while(!Myfile.eof())
                {
                for (int i = 0; i < search.size(); i++)
                        {
                        getline(Myfile,line);
                        if (line[i] == search[i])
                                {
                                isFound = 1;
                                string line_out;
                                line_out = line;
                                cout << line_out << endl;
                                }
                        if(Myfile.eof()&&(!isFound))
                                {
                                cout << "EoGUESS not found" << endl;
                                }
                        }
                }
        Myfile.close();
        }
else
        {
        cout<<"Unable to open this file."<<endl;
        }
return 0;
}


My input file is the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NtsGUESS = 5e10*1e4
EoGUESS = 1e10
EoDEFAULT = 4
NtsDEFAULT = 1e19*1e4
paramSTEP = 1.2,
deltx = 1e-8

Na = 0e16*1e6
Nd = 0e15*1e6
Brad = 1e-10*1e-6
sigma = 1e-12*1e-4
sigmaneutral = sigma*1e-3
&vary = Nd
varystart = 1e16*1e6
varymax = 1e16*1e6
varystep = sqrt(10.0)

T = 300


Usually my output is:

 
EoGUESS = 1e10


However, when I delete that line from the input file, find finds:

 
EoDEFAULT = 4


When I delete both lines, I get as output:

1
2
3
4
EoGUESS not found
EoGUESS not found
EoGUESS not found
EoGUESS not found


So, my question is, how do I make find only find the exact match? Also, if it does not find it, how can I make my code output only one line of not found? Thank you in advance. I just started c++ a few days ago.
So, my question is, how do I make find only find the exact match?

From the title, I would've guessed you were using the find method of a std::string object, but you're not.

Your logic is a bit off. For each character in search you get a line from the file and compare the current letter in search to the corresponding letter in the line you just read. If you want to find only lines that the search pattern appears in:

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

int main()
{
    std::string pattern("EoGUESS");
    std::ifstream in("in1.txt");

    if (!in.is_open())
        std::cout << "Unable to open file\n";

    std::string line;
    bool found = false;
    while (getline(in, line))
    {
        if (std::string::npos != line.find(pattern))
        {
            found = true;
            std::cout << line << '\n';
        }
    }

    if (!found)
        std::cout << '"' << pattern << "\" not found.\n";
}





Your code has quite a few problems, as cire has already pointed out.

But based on this post and your other one...

pass string to double
http://www.cplusplus.com/forum/beginner/168093/

... I think you're going about it in a rather inefficient way. Well, it does looks like you might be intending to search the file for one value at a time.

If you intend to extract multiple values from your configuration file it makes more sense to parse the file once, to extract the values into a std::vector or std::map, and then search the vector or map for your values.

I prefer to use a std:vector (of std::pair values) for this kind of thing, as I want the values in the same order when I save them. std::map sorts the values by key value so imposes alphabetical order which not what I always want (I like to group by category.)

(Aside: in passing I guess you're going to need an expression evaluator, too. You could code your own, but I would consider using an existing library, e.g.
http://muparser.beltoforion.de/ )

Andy

Basic example of how to load name-value pairs from file (using istringstream for testing so C++ Shell friendly) into a vector and then searching vector for required values.

I have kept the approach deliberately basic here and pre-C++11 (e.g. for the search I would have prob use std::find_if() algorithm with a lambda function rather than a for loop.)

I am using a return code to indicate that a value is not found as (a) a blank string might be an acceptable value, (b) raising an exception is not appropriate as successfuly not finding a value is not (necessarily) an error condition. You could use a special string (e.g. "NOT_FOUND") instead but this would prob make the code using the functionality that bit messier.

And it would be much better to encapsulate the functionality in a class.

(Some of code borrowed from other thread...)

Test_SearchConfig with EoGUESS

EoGUESS found at index = 1
EoGUESS = 1e10

Test_SearchConfig with NtsGUESS

NtsGUESS found at index = 0
NtsGUESS = 1e10*1e4

Test_SearchConfig with varystep

varystep found at index = 14
varystep = sqrt(10.0)

Test_SearchConfig with NOT_THERE

NOT_THERE not found


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
// comment this line out to use file
#define TESTING
#include <iostream>
#ifndef TESTING
#include <fstream>
#else
#include <sstream> // use stringstream (rather than fstream) for C++ Shell testing
#endif
#include <string>
#include <vector>

#ifdef TESTING
// pretend file for testing
const char g_testData[] =
"NtsGUESS = 1e10*1e4\n"
"EoGUESS = 1e10\n"
"EoDEFAULT = 4\n"
"NtsDEFAULT = 1e19*1e4\n"
"paramSTEP = 1.2,\n"
"deltx = 1e-8\n"
"\n"
"Na = 1e16*1e6\n"
"Nd = 1e15*1e6\n"
"Brad = 1e-10*1e-6\n"
"sigma = 1e-12 *1e-4\n"
"sigmaneutral = sigma*1e-3\n"
"&vary = Nd\n"
"varystart = 1e16*1e6\n"
"varymax = 1e16*1e6\n"
"varystep = sqrt(10.0)\n"
"\n"
"T = 300";
#endif

typedef std::pair<std::string, std::string> NVP; // Name Value Pair

bool LoadConfig(std::vector<NVP>& configValues);
int SearchConfig(std::vector<NVP>& configValues, const std::string& name, std::string& value);

void Test_SearchConfig(std::vector<NVP>& configValues, const std::string& name);

int main()
{
    std::vector<NVP> configValues;

    if (LoadConfig(configValues))
    {
        Test_SearchConfig(configValues, "EoGUESS");
        Test_SearchConfig(configValues, "NtsGUESS");
        Test_SearchConfig(configValues, "varystep");

        Test_SearchConfig(configValues, "NOT_THERE");
    }
    else
    {
        std::cout << "LoadConfig failed?" << std::endl;
    }

    return 0;
}

// http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring/347974#347974
namespace StringUtils {

const std::string WHITESPACE = " \t";

inline std::string TrimLeft (const std::string& s)
{
    size_t startpos = s.find_first_not_of(WHITESPACE);
    return (startpos == s.npos) ? "" : s.substr(startpos);
}

inline std::string TrimRight (const std::string& s)
{
    size_t endpos = s.find_last_not_of(WHITESPACE);
    return (endpos == s.npos) ? "" : s.substr(0, endpos + 1);
}

inline std::string Trim (const std::string& s)
{
    return TrimRight(TrimLeft(s));
}

bool SplitNameValuePair(const std::string& s, char c, std::string& name, std::string& value)
{
    std::string::size_type pos = s.find(c);
    if (pos == std::string::npos)
        return false;

    name  = Trim(s.substr(0, pos));
    value = Trim(s.substr(pos + 1));

    return true;
}

} // end namespace StringUtils

bool LoadConfig(std::vector<NVP>& configValues)
{
#ifndef TESTING
    // use constructor which opens file rather than default
    // constructor plus open() method
    std::ifstream Myfile("in1.txt");
    if(!Myfile.is_open())
    {
        // this way round the logic works better with the #ifdefs
        std::cout << "Unable to open this file." << std::endl;
        return false;
    }
#else
    std::istringstream Myfile(g_testData);
#endif
    std::string line;
    int lineNum = 0;
    while (getline(Myfile, line))
    {
        ++lineNum;
        line = StringUtils::Trim(line);
        if (line.empty()) // skip blank lines
            continue;
        std::string name;
        std::string value;
        if (StringUtils::SplitNameValuePair(line, '=', name, value))
        {
            name = StringUtils::Trim(name);
            value = StringUtils::Trim(value);
            configValues.push_back(std::make_pair(name, value));
        }
        else
        {
            std::cout << "skipping line " << lineNum << std::endl;
        }
    }
    // no need for Myfile.close() as ifstream destructor handles this
    return true;
}

int SearchConfig(std::vector<NVP>& configValues, const std::string& name, std::string& value)
{
    value.clear();

    const int count = configValues.size();

    for (int index = 0; index < count; ++index)
    {
        if (configValues[index].first == name)
        {
            value = configValues[index].second;
            return index;
        }
    }

    return -1; // not found
}

void Test_SearchConfig(std::vector<NVP>& configValues, const std::string& name)
{
    std::cout << "Test_SearchConfig with " << name << std::endl;
    std::cout << std::endl;

    std::string value;
    int found_index = SearchConfig(configValues, name, value);

    if (-1 == found_index)
    {
        std::cout << name << " not found" << std::endl;
    }
    else
    {
        std::cout << name << " found at index = " << found_index << std::endl;
        std::cout << name << " = " << value << std::endl;
    }

    std::cout << std::endl;
}
Last edited on
Hey, thanks a lot guys. This is more help than I could have hoped for. I've only been learning c++ for about a week or so, so it will take me some time to decipher this, but this looks like exactly what I needed.

Andy, it does make a lot more sense to parse once. Since the main program takes a couple of hours to run though, even on multiple cores on an HPC cluster, the delay in parsing is not that big of a deal. If I had written the main code myself, I probably would have used FORTRAN instead of c++ (since that is what most scientific programs are written in, for efficiency), but I am glad I'm having a chance to learn c++, as it is so much more useful in more scenarios, not to mention much more marketable.
Topic archived. No new replies allowed.