Searching text-document for a specific sentence

Pages: 12
I'm unsure of how to search a text-document using fstream for a whole sentence, without actually searching for that whole sentence, for example.

Home |Welcome Home!|

Is there a way I can search for Home, then display everything between the |'s? Or am I trying to do this the wrong way?

Thank you!
You can do whatever you can imagine! When I have a new programming problem I usually think in depth about the exact steps I would take to do whatever the task is as a human then translate that into a programming language using the logic available, I have yet to come across something quantifiable that hasn't translated.
Thanks! I suppose I'm at a loss as how to start, something like this?
1
2
3
4
5
ifstream textDocument;
textDocument.open("FILE.TXT");
std::string loadWord;
textDocument >> loadWord;
if ( loadWord == "Home" && != "|" ) {std::cout << textDocument " ";}
There is indeed a way to do this the way you are thinking. We just need to think about what constitutes a sentence. In your example I assume you want to return any sentence that contains the word "Home" in it correct?

There is actually a few ways to do this (As with anything in programming), one way you could first extract every sentence in the document into it's own std::string then just search through them strings to find which one's contain the word "Home". This is probably the easiest way to do so.

Or another way is we would just search the document for the word "Home" (You can handle case how you want to) and once we find out we need to extract that whole sentence.

To extract the sentence we would need to find the previous punctuation mark the ends the previous sentence the the punctuation mark that end the sentence that "Home" is in.

For example.
Hello how are you doing? It is good to have you home!
                       ^                            ^
                       |                            |
                      Last                     Current


You would then extract the sentence between those two punctuation (Remember to remove the beginning whitespace in the sentence).

Here is a quick example which uses the first option I discussed (About splitting the document into strings and then finding which contains "Home").

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>
#include <vector>

int main()
{
    // This would be the string you keep what you extracted from the file in.
    const std::string words("Hello how are you? It is great to have you home! I am really just home. More testing. I am just testing, home!");
    
    // This is the word we are looking for. In this example home != Home, or in other words it is case sensitive.
    const std::string searchWord("home");
    
    // This will hold all the sentences.
    std::vector<std::string> sentences;
    
    // Keeps track of where we are in the words string.
    std::size_t startingPosition = 0;
    
    // Split the std::string into multiple strings that each represent a sentence.
    std::size_t result = words.find_first_of(".!?");
    while (result != std::string::npos)
    {
        sentences.push_back(words.substr(startingPosition, (result + 1) - startingPosition));
        startingPosition = result + 2; // We dp 2 so we don't get the starting whitespace.
        
        result = words.find_first_of(".!?", startingPosition);
    }
    
    // Find which sentences contain the word "home" and then print them.
    for (std::size_t i = 0; i != sentences.size(); ++i)
    {
        std::size_t helloResult = sentences[i].find(searchWord);
        if (helloResult != std::string::npos)
        {
            std::cout << sentences[i] << std::endl;
        }
    }
    
    return 0;
}


This is just quickly whipped up so there is going to be some obvious improvements that can be made (Like handling case and error handling) but I will leave that to you. Hopefully this will give you some ideas on how to work this out. Let me know if you have any other questions.

That's very good! What I'm basically trying to accomplish is I have a game map, and when I move in this map, I would like to display a brief description of the new area. For instance, if I move, and the current Location is HOME, I would like to print out the description for HOME, and nothing more. I THINK I can do that with the information you provided, although It might take me some fooling around to do so. Thank you for responding!

Is there a sample you could give me of how I would accomplish what I'm trying to do? I get the logic but the syntax is throwing me for a loop ( ba dum tish ).
Last edited on
MaxDaniels wrote:
Is there a sample you could give me of how I would accomplish what I'm trying to do? I get the logic but the syntax is throwing me for a loop ( ba dum tish ).


Sure let's look at the example you provided again now that I know you aren't looking to extract normal sentences.

Home |Welcome Home!|
Store |Welcome to the store!|


Now in this example I assume the first word is what you will use to look up the sentence to display. The '|' characters are used to show where the sentence starts and where it ends.

So basically there will be two steps in extracting the sentence you wish to display.

1. Look up the "keyword" for the location you just entered. This will be the first word ("Home" or "Store").

2. Once we have the keyword look we then need to extract the sentence that comes after that keyword by utilizing the | characters.

The easiest way to accomplish this would be to make sure the file that contains the information is in a format like so.

Home |Welcome home!|
Store |Welcome to the store!|
Wilderness |Get ready to die!|


Basically one per line.

Here is some example code on how you could read this info from a file that is formatted like this. In this example I will be using a std::map to hold the information. This is a container that is perfectly suited for this problem so if you aren't familiar with it I would look up on it. A map is a key value pair container so you give it a key ("Home") and it returns the value for that key ("Welcome home!").


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

int main()
{
    // We assume the file is in the format noted above.
                             
    // This is our map which holds the values.
    std::map<std::string, std::string> sentences;
    
    // Loop through the file and read each line into the map.
    std::ifstream file("testing.txt");
    while (file.is_open())
    {
        std::string line;
        
        // Loop through each line.
        while (std::getline(file, line))
        {
            // Once we have the line we need to parse the information.
            
            // First we get the keyword for the map ("Home").
            // We know this is the first word on the line so we just search for the first whitespace.
            // And copy everything inbetween the first character and that whitespace.
            std::string keyword = std::string(line, 0, line.find_first_of(" "));
            
            // Next we need to find the sentence to display for that keyword.
            // First we find the first | character. first holds the position of that charater.
            std::size_t first = line.find_first_of("|");
            
            // Next we need to find the second | character. We start the search 1 past the first | character.
            // Another way would be to use find_last_of() which searchs the string backwards.
            std::size_t second = line.find_first_of("|", first + 1);
            
            // Now after we have those positions we just extract the sentence in between them.
            // We do first + 1 and (second - first) - 1 because we don't want the | in the sentence.
            std::string sentence = std::string(line, first + 1, (second - first) - 1);
            
            // Then we add them to the map.
            sentences[keyword] = sentence;
        }
        
        file.close();
    }
    
    
    // Our map now contains all the information and we can access it like so.
    std::string keywordInput;
    std::cin >> keywordInput;
    
    // We do some error checking to make sure the input is correct.
    if (sentences.find(keywordInput) != sentences.end())
    {
        std::cout << sentences[keywordInput];
    }

    return 0;
}


Again this is just a example program so there is improvements that can be made but hopefully this will give a idea of what to do.
Last edited on
That is spectacular! I will need to study some of this as it is new to me. Thank you for your help! Now that I know how to accomplish what my brain is thinking, is this a pretty effective way to accomplish what i'm wanting in the best manner/easiest way to code, possible?
From the information I have yes it is a great way of doing it. Keeping the information in a file like this will make it much easier to add new keywords and sentences to display (All you need to do is add another line in the correct format to the file).

If you were working with a designer this would be a big plus since the game designer would be able to edit all the sentences very easily without needing to do anything with code.

In fact most professional games will keep almost all of their dialog in files and then parse those files (Kinda like you are doing here). They do this because as stated before the designers don't need to know how to code to edit stuff, there is no compiling every time a change is made and it makes it much easier to do localization.

Localization if you don't know means tailoring the games dialog (and other things) for a certain region of the world. In your example they would basically have multiple different text files for each region and would then change what they need to for example.

English Version
Home |Welcome Home!|
Store |Welcome to the store!|


Spanish Version (I used Google so don't fault me if it is wrong ;p)
Home |Bienvenido a Casa!|
Store |Bienvenido a la tienda!|
That's awesome, yeah I feel as though the story should be kept seperate from the actual program, hardcoding the story mode doesn't seem like a good idea as far as organization (crowds the hell out of source code ) AND updating the story.

I'm having a bit of trouble, been fiddling with it, not sure what i'm doing wrong.
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
void map::displayAreaDescription()
{
     std::ifstream load_description;
     load_description.open("description.txt");
     std::map<std::string, std::string> sentences;
     
     while ( load_description.is_open() )
     {
           std::string line;
           
           while ( std::getline(load_description, line) )
           {
                  std::string keyword = std::string(line, 0, line.find_first_of(" "));
                  std::size_t first = line.find_first_of("|");
                  std::size_t second = line.find_first_of("|", first + 1);
                  std::string sentence = std::string(line, first + 1, (second - first) - 1);
                  sentences[keyword] = sentence;
                  std::cout << keyword;
                  }
                  load_description.close();
           }
           if ( sentences.find(currentLocation) != sentences.end() )
           {
                std::cout << sentences[currentLocation];
                }
     
}

Last edited on
By the way thank you so much, can't say how much I appreciate it! I consider myself pretty novice, but I am going for a degree in Computer Science, hopefully to be a programmer. So words of wisdom saying I'm developing like a professional ' I know there is so much I don't know' but i'm getting there! Lol.
Last edited on
Still having a problem getting it to work correctly, is there an error somewhere I can't see?

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
void map::displayAreaDescription()
{
     std::map<std::string, std::string> sentences;
     
     std::ifstream load_description("description.txt");
     while ( load_description.is_open() )
     {
           std::string line;
           
           while ( std::getline(load_description, line) )
           {
                 currentLocation = std::string(line, 0, line.find_first_of(" "));
                 std::size_t first = line.find_first_of("|");
                 std::size_t second = line.find_first_of("|", first + 1);
                 std::string sentence = std::string(line, first + 1, (second - first) - 1);
                 sentences[currentLocation] = sentence;
                 }
                 load_description.close();
           }
           
           std::string keywordInput = currentLocation;
           
           if (sentences.find(keywordInput) != sentences.end())
    {
        std::cout << sentences[keywordInput];
    }

}
MaxDaniels wrote:
I'm having a bit of trouble, been fiddling with it, not sure what i'm doing wrong.


No problem, from what I can tell from the code here is what I see. I am just guessing at the problem right now but if you can give the error you are receiving (Or if no error what you are having trouble with) I could pin down the problem further.

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
// I added a function parameter see below for details.
void map::displayAreaDescription(const std::string& currentLocation)
{
    std::ifstream load_description;
    load_description.open("description.txt");
    std::map<std::string, std::string> sentences;
     
    while ( load_description.is_open() )
    {
        std::string line;
           
        while ( std::getline(load_description, line) )
        {
            std::string keyword = std::string(line, 0, line.find_first_of(" "));
            std::size_t first = line.find_first_of("|");
            std::size_t second = line.find_first_of("|", first + 1);
            std::string sentence = std::string(line, first + 1, (second - first) - 1);
            sentences[keyword] = sentence;
            std::cout << keyword;
        }    // Always try and keep the brackets aligned makes it easier to read.
        load_description.close();
    }
    
    // Now the problem lies here. We are using currentLocation but we haven't defined it in this scope.
    // Unless you have defined currentLocation in a global scope the function doesn't doesn't know what it is.
    // I would suggest passing in currentLocation as a function parameter like I did up top.
    // Since you did this in a function it is a bit different then having it all in main().

    if ( sentences.find(currentLocation) != sentences.end() )
    {
        std::cout << sentences[currentLocation];
    }
}
Last edited on
I have currentLocation defined in my map class in public, so It should have access to it I believe.
Last edited on
Ohh my bad on that one. Didn't see that it was a method instead of a function.

How is it not working properly? Is it a giving a compiler error, or is it just displaying the information incorrectly? Is it not displaying anything at all?
Nothing at all, but no error, and format in text-file is correct, Yeah I'm not sure, let me post what I have.

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
void map::displayAreaDescription()
{
     std::map<std::string, std::string> sentences;
     
     std::ifstream load_description("description.txt");
     while ( load_description.is_open() )
     {
           std::string line;
           
           while ( std::getline(load_description, line) )
           {
                 currentLocation = std::string(line, 0, line.find_first_of(" "));
                 std::size_t first = line.find_first_of("|");
                 std::size_t second = line.find_first_of("|", first + 1);
                 std::string sentence = std::string(line, first + 1, (second - first) - 1);
                 sentences[currentLocation] = sentence;
           }
                 load_description.close();
     }
           
     std::string keywordInput = currentLocation;
           
     if (sentences.find(keywordInput) != sentences.end())
        {
           std::cout << sentences[keywordInput];
        }

}


and the text file
HOME |Welcome home!|

and currentLocation = "HOME"
Last edited on
Things like this that make me want to bang my head on a wall lol, for the life of me I can't figure it out.
Have you stepped through the code? Do you know if line 5 is actually successful in opening the file and that line 6 ever evaluates to true? (Why is that a loop beginning on line 5, anyway?)
Ok well the first thing I see is that on line 12 you shouldn't be using currentLocation to store that value. Make a new variable to hold that we don't want to store it in currentLocation.

The reason why is because the currentLocation member in your map class is I assume meant to hold where the player is currently at. Whereas the one in the displayAreaDescription() function is used to temporary hold the keyword for all the locations. So we we use currentLocation in that function right there it will mess up where the player is at.

So change that to something like std::string location = std::string(line, 0, line.find_first_of(" "));.

You can also then delete line 21 and change line 23 and 25 to be currentLocation since we want it to display what the current locations text is.

For example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
std::map<std::string, std::string> sentences;
     
     std::ifstream load_description("description.txt");
     while ( load_description.is_open() )
     {
           std::string line;
           
           while ( std::getline(load_description, line) )
           {
                 std::string location = std::string(line, 0, line.find_first_of(" "));
                 std::size_t first = line.find_first_of("|");
                 std::size_t second = line.find_first_of("|", first + 1);
                 std::string sentence = std::string(line, first + 1, (second - first) - 1);
                 sentences[location] = sentence;
           }
                 load_description.close();
     }
           
           
     if (sentences.find(currentLocation) != sentences.end())
        {
           std::cout << sentences[currentLocation];
        }


Other then that I don't see anything wrong with it and after making those changes it runs fine for me (Works fine without those changes except for the reasons mentioned above).

So I have a feeling that it has to do with not being able to find the text file. Make sure the text file is placed either in the same folder as the .exe file generated or in your base projects directory (Where the solution file will be). This varies from compiler to compiler if you can let me know which compiler you are on I can tell you exactly where to place it.

If that doesn't work you need to do some debugging in your debugger or place some std::cout statements around like below to see where execution stops and what the values are.

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
std::map<std::string, std::string> sentences;
     
     std::ifstream load_description("testing.txt");
     while ( load_description.is_open() )
     {
         // Make sure the file is opening
         std::cout << "File is open" << std::endl;
         
           std::string line;
           
           while ( std::getline(load_description, line) )
           {
                 std::string location = std::string(line, 0, line.find_first_of(" "));
                 std::size_t first = line.find_first_of("|");
                 std::size_t second = line.find_first_of("|", first + 1);
                 std::string sentence = std::string(line, first + 1, (second - first) - 1);
                 sentences[location] = sentence;
                 
                 // Get the keyword and sentence for each othem
                 std::cout << "Keyword: " << location << std::endl;
                 std::cout << "Sentence: " << sentence << std::endl;
           }
                 load_description.close();
     }
           
     
     // Got done with parsing the file
     std::cout << "Done parsing" << std::endl;
     if (sentences.find(currentLocation) != sentences.end())
        {
           std::cout << sentences[currentLocation];
        }
        
     // Can't find the keyword in the map
     std::cout << "Can't find map keyword if nothing prints above" std::endl;
Last edited on
Oh my god I feel dumb. My "description.txt" was actually saved as "description.txt.txt".

Thank you so much for helping with that, boy do I feel dumb. But.. you were right, it WAS the file!
No problem :p that has happened to me many many time also, glad I could be of some help :).
Pages: 12