Thread: ifstream text file search for word and gather block of data

  1. #1
    Registered User
    Join Date
    Aug 2010
    Posts
    14

    Smile ifstream text file search for word and gather block of data

    Hi all,

    I am a relatively inexperienced programmer, but I am really eager to do the hard yards and learn. To produce the code below, I have spent about 4 hours reading textbooks and searching the internet, so try not to laugh at it! Thanks in advance for any advice you may offer! My problem is...

    I have a text file with the following text inside it
    Code:
    image 0
    281253 6.13325e+06 34.8823 0.009 
    -3.13852 0.0117609 -1.07777
    1170 1189 1279 1278 ...
    
    ... (and then the next lot of information that is correlated with the next image)
    image 1
    234940 6.13232e+06 ...etc
    So what I want to do is:
    1. Search the code for the text "image"
    2. Store the number after the text "image"
    3. Store all of the following numbers, until the next mention of the word "image" OR until the end of the file.

    So my attempt was to do this:
    Code:
    #include <iostream>
    #include <conio.h>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    int imageNumber = -1;
    
    ifstream inFile;
    string searchString = "image";
    string lineOfText;
    
    inFile.open("C:\\datafile.txt");
    
    for(;;){
        getline(inFile, lineOfText);
    		  
        if (inFile.eof()) break;
        if (lineOfText.find(searchString, 0) != string::npos){
    	cout << "The program found the word: " << searchString << endl;
    	inFile >> imageNumber;
    	cout << "imageNumber read: " << imageNumber << "\n";
    		  	  
    	/* etc...
    	I read in the other numbers using:
    	inFile >> variableName;
    	and then read in the 4 digit numbers into an array using
    	a for loop (which works well)
    	 */
         }
    }
    getch();
    return 0;
    }
    The output (which is not quite what I want) is:
    Code:
    The program found the word: image
    imageNumber read: 281253
    I want the imageNumber to read the number after the text "image", i.e. '0' in this case. The problem is because the lineOfText.find() has searched the whole line, and now the image stream pointer (correct me if I am wrong ) is pointing to the next line, so imageNumber just picks up the first integer on that line.

    I never like coming to people for help with empty hands, so I think I either need to:
    1. Use tokenisers - I read the forum help on this and vaguely understood it (it'll take me another 4 hours or so to work out how to integrate search with them though...)
    2. Stop the pointer after matching the word "image" so I can read in the correct imageNumber variable. I have no idea how. Maybe seekg()? I read about that a few times.

    Once again, I am only a beginner, so I would prefer you to help me with this code if possible rather than having to get my head around another concept.

    Thanks so much for any suggestions (particularly code).

    Regards, Geek10

  2. #2
    Anti-Poster
    Join Date
    Feb 2002
    Posts
    1,401
    Note that when you getline(), you get the whole line of text. When you infile >> imageNumber;, it gets the number after the previous line of text. The number you want is in lineOfText.
    Code:
    #include <iostream>
    #include <string>
    
    int main()
    {
       std::string searchString("image");
       std::string lineOfText("image 0");
       std::string::size_type i = lineOfText.find(searchString, 0);
       if (i != std::string::npos)
       {
          std::cout << lineOfText.substr(i + searchString.size() + 1) << std::endl;
       }
    
       return 0;
    }
    Quote Originally Posted by Geek10 View Post
    I never like coming to people for help with empty hands
    This is a good attribute to have when asking questions here. People will appreciate it.
    If I did your homework for you, then you might pass your class without learning how to write a program like this. Then you might graduate and get your degree without learning how to write a program like this. You might become a professional programmer without knowing how to write a program like this. Someday you might work on a project with me without knowing how to write a program like this. Then I would have to do you serious bodily harm. - Jack Klein

  3. #3
    Registered User
    Join Date
    Aug 2010
    Posts
    14

    Thumbs up Thanks for your explanation!

    Hi pianorain,

    Thanks for your input! I have muddled through your code for the past hour and finally realised that:
    Code:
    std::string::size_type i = lineOfText.find(searchString, 0);
    means
    1. Starting from the very beginning of the string (0),
    2. find the string searchString in the string lineOfText
    3. store the location of that string to size_type i.

    So I have finally worked out what your code fully means, I think. Below is it expanded, and commented. Hopefully others find it useful as well.
    Code:
    #include <iostream>
    #include <string>
    #include <conio.h>
    #include <sstream>
    //#include <math.h>
    //#include <stdio.h>
    
    
    int main()
    {
       std::string searchString("image");
       std::string lineOfText("image 236");
    	
    	// size_type i is the location of the first string matching searchString found by .find
    	std::string::size_type i = lineOfText.find(searchString, 0);
       // searchString.size() is the number of letters in searchString = 5
    	std::cout << "searchString.size() " << searchString.size() << "\n";
       // string::npos signals the end of the string?
    	std::cout << "std::string::npos " << std::string::npos << "\n";
       std::cout << "i " << i << "\n";
    	if (i != std::string::npos) 
       {	// If the location of the first string is not at the end of the string
          std::cout << "The string after the word image: " << lineOfText.substr(i + searchString.size() + 1) << std::endl;
       }
       
    	// This makes a string out of the characters following the space after "image"
    	std::string numberString = lineOfText.substr(i + searchString.size() + 1);
    	// stringstream provides an interface to manipulate strings as if they were input/output streams.
    	std::stringstream numberStream(numberString);
    	
    	
    	int imageNumber;	// Initialise the integer for the found value
    	if((numberStream >> imageNumber).fail()){	// Convert stream to integer
     	    std::cout << "ERROR: Failed string to integer conversion";
       }
      	std::cout << "imageNumber integer: " << imageNumber << std::endl;
    
    	getch();
       return 0;
    }
    Thanks so much pianorain, this thread can be marked solved (if this forum does that).

    Sorry about the wonky code there - it looks horrible - my code editor (Dev C++) is playing up a bit - I must have a setting wrong somewhere.
    Regards,

    Geek10
    Last edited by Geek10; 09-01-2010 at 11:59 PM. Reason: coding is wonky

  4. #4
    Novice
    Join Date
    Jul 2009
    Posts
    568
    Just for the future.
    Code:
    // It's better to do this...
    while (1) {
        ...
        if (condition) break;
        ...
    }
    
    // ...rather then this.
    for (;;) {
        ...
        if (condition) break;
        ...
    }
    
    // Or, in your case...
    while (!inFile.eof()) {
        ...
    }

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by msh
    Code:
    // It's better to do this...
    while (1) {
        ...
        if (condition) break;
        ...
    }
    
    // ...rather then this.
    for (;;) {
        ...
        if (condition) break;
        ...
    }
    This is a matter of subjective style, and I disagree that the former is better than the latter.

    Quote Originally Posted by msh
    Code:
    // Or, in your case...
    while (!inFile.eof()) {
        ...
    }
    No, that is wrong. Unless you take care to do it properly, eof() should not be used to control a loop. Rather:
    Code:
    while (getline(inFile, lineOfText)) {
        // The eof() check is no longer needed.
        // ...
    }
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Actually there is a reason why the OP's is at least closer to correct, and why while(!inFile.eof()) is wrong. It's a FAQ question after all. In the OP's code, he tries to read before he tests for EOF, which is important. Otherwise it really doesn't work. For example, try reading a completely empty file like that.

    EOF isn't the only time to stop reading from a stream either, so there you have it.

  7. #7
    Novice
    Join Date
    Jul 2009
    Posts
    568
    Quote Originally Posted by laserlight View Post
    This is a matter of subjective style, and I disagree that the former is better than the latter.
    Is this entirely subjective, or is there a reason for using for( ;; )? I've seen it used, and I understand that it's valid, but I couldn't understand why it's used over while(1).

    Quote Originally Posted by laserlight View Post
    No, that is wrong. Unless you take care to do it properly, eof() should not be used to control a loop. Rather:
    Code:
    while (getline(inFile, lineOfText)) {
        // The eof() check is no longer needed.
        // ...
    }
    My bad, and noted for future. I rushed the answer.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by msh
    Is this entirely subjective, or is there a reason for using for( ;; )? I've seen it used, and I understand that it's valid, but I couldn't understand why it's used over while(1).
    A long time ago in a galaxy far, far away... the for loop version was better than the while loop version because compilers might evaluate the constant expression instead of recognising the infinite loop.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Apr 2009
    Posts
    12

    Post Full Code

    Just having a look at this topic for reference about searching files through fstream, the code looks quite nice here, and I now understand the concept.

    Thanks!

    Also, I noticed that the full code was never posted quite completely, so I have taken the liberty to do so below.

    NOTE: I have also removed an unneeded library and fixed up the indentation;

    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <fstream>
    
    using namespace std;
    
    int main(){
        int imageNumber = -1;
    
        ifstream inFile("data.txt");
        string searchString = "image";
        string lineOfText = "image 236";
    
        // size_type i is the location of the first string matching searchString found by .find
        string::size_type i = lineOfText.find(searchString, 0);
    
        // searchString.size() is the number of letters in searchString = 5
        cout << "searchString.size() " << searchString.size() << "\n";
    
        // string::npos signals the end of the string?
        cout << "string::npos " << string::npos << "\n";
        cout << "i " << i << "\n";
    
        if (i != string::npos){    // If the location of the first string is not at the end of the string
            cout << "The string after the word image: " << lineOfText.substr(i + searchString.size() + 1) << endl;
        }
    
        // This makes a string out of the characters following the space after "image"
        string numberString = lineOfText.substr(i + searchString.size() + 1);
    
        // stringstream provides an interface to manipulate strings as if they were input/output streams.
        stringstream numberStream(numberString);
    
    
        imageNumber=0;    // Initialise the integer for the found value
        if((numberStream >> imageNumber).fail()){    // Convert stream to integer
            cout << "ERROR: Failed string to integer conversion";
        }
        cout << "imageNumber integer: " << imageNumber << endl;
    
        cout << endl << "Press ENTER to continue" << endl;
        cin.clear();
        cin.sync();
        cin.ignore();
    }
    Last edited by Panarchy; 10-06-2010 at 12:18 PM.

  10. #10
    Registered User
    Join Date
    Aug 2010
    Posts
    14

    No probs!

    Panarchy, I'm glad to see my code is helpful to someone.

    What does your cin.sync do? According to C++ Reference, "Synchronizes the buffer associated with the stream to its controlled input sequence. This effectively means that the unread characters in the buffer are discarded."

    Why are characters unread, let alone discarded?

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Geek10
    What does your cin.sync do? According to C++ Reference, "Synchronizes the buffer associated with the stream to its controlled input sequence. This effectively means that the unread characters in the buffer are discarded."

    Why are characters unread, let alone discarded?
    User input is stored in a buffer to be read, so the idea here is to discard whatever is left on the input buffer, and then read and ignore a character, in an attempt (that is generally misguided) to keep the console window open after the process (program) has terminated.

    That said, cin.sync() is not guaranteed to do this, i.e., whatever C++ reference you are using, it is giving information that may be usually true, but not guaranteed to be true, hence it is actually not portable.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Binary Search of Array
    By pantherman34 in forum C Programming
    Replies: 21
    Last Post: 05-04-2010, 09:39 AM
  2. Unknown memory leak with linked lists...
    By RaDeuX in forum C Programming
    Replies: 6
    Last Post: 12-07-2008, 04:09 AM
  3. Template overload of operator ++/--
    By Elysia in forum C++ Programming
    Replies: 26
    Last Post: 10-23-2007, 08:45 AM
  4. semaphores
    By Dr Spud in forum C Programming
    Replies: 7
    Last Post: 09-22-2007, 12:45 PM
  5. HUGE fps jump
    By DavidP in forum Game Programming
    Replies: 23
    Last Post: 07-01-2004, 10:36 AM

Tags for this Thread