find lines (from a file) that contain a specified word - c++

I cannot figure out how to list out the lines that contain a specified word. I am provided a .txt file that contains lines of text.
So far I have come this far, but my code is outputting the amount of lines there are. Currently this is the solution that made sense in my head:
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
void searchFile(istream& file, string& word) {
string line;
int lineCount = 0;
while(getline(file, line)) {
lineCount++;
if (line.find(word)) {
cout << lineCount;
}
}
}
int main() {
ifstream infile("words.txt");
string word = "test";
searchFile(infile, word);
}
However, this code simply doesn't get the results I expect.
The output should just simply state which lines have the specified word on them.

So, to sum up the solution from the comments. It is just about the std::string's find member function. It doesn't return anything compatible with a boolean, it either return an index if found, or std::string::npos if not found, which is a special constant.
So calling it with traditional way if (line.find(word)) is wrong, but instead, it should be checked this way:
if (line.find(word) != std::string::npos) {
std::cout << "Found the string at line: " << lineCount << "\n";
} else {
// String not found (of course this else block could be omitted)
}

Related

Need to read a line of text from an archive.txt until "hhh" its found and then go to the next line

My teacher gave me this assignment, where I need to read words from a file and then do some stuff with them. My issue is that these words have to end with "hhh", for example: groundhhh, wallhhh, etc.
While looking for a way to do this, I came up with the getline function from the <fstream> library. The thing is that getline(a,b,c) uses 3 arguments, where the third argument is to read until c is found but c has to be a char, so it doesn't work for me.
In essence, what I'm trying to achieve is reading a word from a file, "egghhh" for example, and make it so if "hhh" is read then it means the line finishes there and I receive "egg" as the word. My teacher used the term "sentinel" to describe this hhh thing.
This is my try:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
ifstream read_archive;
void showListWords(){
string word;
string sentinel = "hhh";
while (getline(read_archive,word,sentinel)){
cout << word << endl;
}
}
void openReadArchive(){
read_archive.open("words.txt");
if(read_archive.fail())
cout << "There is something wrong with the archive" << endl;
}
As you discovered, std::getline() allows you to specify only 1 char for the "sentinel" that it stops reading on, where '\n' (line break) is the default.
What you can do is use this default to read an entire line of text from the file into a std::string, then put that string into an std::istringstream and read words from that stream until the end of the stream is reached or you find a word that ends with "hhh", eg:
#include <sstream>
#include <limits>
void showListWords()
{
string line, word;
string sentinel = "hhh";
while (getline(read_archive, line))
{
istringstream iss(line);
while (iss >> word)
{
if (word.length() > sentinel.length())
{
string::size_type index = word.length() - sentinel.length();
if (word.compare(index, sentinel.length(), sentinel) == 0)
{
word.resize(index);
iss.ignore(numeric_limits<streamsize>::max());
}
}
cout << word << endl;
}
}
}
In which case, you could alternatively just read words from the original file stream, stopping when you find a word that ends with "hhh" and ignore() the rest of the current line before continuing to read words from the next line, eg:
#include <limits>
void showListWords()
{
string word;
string sentinel = "hhh";
while (read_archive >> word)
{
if (word.length() > sentinel.length())
{
string::size_type index = word.length() - sentinel.length();
if (word.compare(index, sentinel.length(), sentinel) == 0)
{
word.resize(index);
read_archive.ignore(numeric_limits<streamsize>:max, '\n');
}
}
cout << word << endl;
}
}

Finding pattern in a text in C++

I have written the following code to find the number of "ATA" in a text that is read to a string as "GCTATAATAGCCATA". The count returned should be 3 but it returns 0. When I check in debugger the string for text is initially created. However, when an empty string is passed to the function patternCount. Am I reading the contents of the file into the string text correctly?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void patternCount(string text, string pattern);
int main()
{
string text;
fstream file_("test.txt");
if(file_.is_open())
{
while(getline(file_,text))
{
cout << text << '\n';
}
file_.close();
}
cout << "Enter a string ";
string pattern;
getline(cin, pattern);
patternCount(text, pattern);
return 0;
}
void patternCount(string text, string pattern)
{
int count = 0;
size_t nPos = text.find(pattern, 0);
while (nPos != string::npos)
{
nPos = text.find(pattern, nPos + 1);
++count;
}
cout << "There are " << count <<" " << pattern << " in your text.\n";
}
This code just counts the number of occurrence of input string in the last line of text file. If that line is empty or no does not contain the string, The output result will be 0.
But I guess the OP wants to search a whole file, in which case the main function need be fixed accordingly.
std::ifstream file{"test.txt"};
std::ostringstream text;
std::copy(std::istream_iterator<char>{file}, std::istream_iterator<char>{},std::ostream_iterator<char>{text});
//...
patternCount(text.str(), pattern);
So if I understand correctly, you're not sure if you're reading correctly the contents from the file test.txt. If you want to read every content, then try this instead:
ifstream file_("test.txt");
string s,text;
file_>>s;
text=s;
while(file_>>s)
{
text=text+" "+s;
}
This should probably work. Note that reading from a file like filename>>string only reads till the first space. That's why I'm using the while. You can also use getline(), which reads the whole text with spaces. Also note that you should include fstream. Printing out the text should help more as well.
#include <iostream>
#include <fstream>
#include <string>
using std::cout;
using std::cerr;
using std::string;
int count = 0; // we will count the total pattern count here
void patternCount(string text, string pattern);
int main() {
cout << "Enter a string ";
string pattern;
std::getline(cin, pattern);
string text;
fstream file_("test.txt");
if(file_.is_open()){
while(std::getline(file_,text))
patternCount(text,pattern);
file_.close();
}else
cerr<<"Failed to open file";
cout << "There are " << count <<" " << pattern << " in your text.\n";
return 0;
}
void patternCount(string text, string pattern){
size_t nPos = text.find(pattern, 0);
while (nPos != string::npos) {
nPos = text.find(pattern, nPos + 1);
++count;
}
}
The Problem
Your code was good, there were no bugs in patternCount function.
But You were reading the file in an incorrect way. See, everytime you call std::getline(file_, text), the old result of the _text are overwritten by new line. So, in the end of the loop, when you pass text to patternCount function, your text only contains the last line of the file.
The Solution
You could have solved it in two ways:
As mentioned above, you could run patternCount() to each line in while loop and update a global count variable.
You could append all the lines to text in while loop and at last call the patternCount function.
Whichever you prefer, I have implemented the first, while second one is in other answers.

Find specific text in string delimited by newline characters

I want to find a specific string in a list of sentence. Each sentence is a line delimited with a \n. When the newline is reached the current search should stop and start new on the next line.
My program is:
#include <iostream>
#include <string.h>
using namespace std;
int main(){
string filename;
string list = "hello.txt\n abc.txt\n check.txt\n"
cin >> filename;
// suppose i run programs 2 times and at 1st time i enter abc.txt
// and at 2nd time i enter abc
if(list.find(filename) != std::string::npos){
//I want this condition to be true only when user enters complete
// file name. This condition also becoming true even for 'abc' or 'ab' or even for 'a' also
cout << file<< "exist in list";
}
else cout<< "file does not exist in list"
return 0;
}
Is there any way around. i want to find only filenames in the list
list.find will only find substring in the string list, but if you want to compare the whole string till you find the \n, you can tokenize the list and put in some vector.
For that, you can put the string list in std::istringstream and make a std::vector<std::string> out of it by using std::getline like:
std::istringstream ss(list);
std::vector<std::string> tokens;
std::string temp;
while (std::getline(ss, temp)){
tokens.emplace_back(temp);
}
If there are leading or trailing spaces in the tokens, you can trim the tokens before adding them to the vector. For trimming, see What's the best way to trim std::string?, find a trimming solution from there that suits you.
And after that, you can use find from <algorithm> to check for complete string in that vector.
if (std::find(tokens.begin(), tokens.end(), filename) != tokens.end())
std::cout << "found" << std::endl;
First of all I wouldn't keep the list of files in a single string, but I would use any sort of list or vector.
Then if keeping the list in a string is a necessity of yours (for some kind of reason in your application logic) I would separate the string in a vector, then cycle through the elements of the vector checking if the element is exactly the one searched.
To split the elements I would do:
std::vector<std::string> split_string(const std::string& str,
const std::string& delimiter)
{
std::vector<std::string> strings;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = str.find(delimiter, prev)) != std::string::npos)
{
strings.push_back(str.substr(prev, pos - prev));
prev = pos + 1;
}
// To get the last substring (or only, if delimiter is not found)
strings.push_back(str.substr(prev));
return strings;
}
You can see an example of the function working here
Then just use the function and change your code to:
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
int main(){
string filename;
string list = "hello.txt\n abc.txt\n check.txt\n"
cin >> filename;
vector<string> fileList = split_string(list, "\n");
bool found = false;
for(int i = 0; i<fileList.size(); i++){
if(fileList.at(i) == file){
found = true;
}
}
if(found){
cout << file << "exist in list";
} else {
cout << "file does not exist in list";
}
return 0;
}
Obviously you need to declare and implement the function split_string somewhere in your code. Possibly before main declaration.

Read file into array and return it from a function C++

In Lua, I have such a function to read a file into an array:
function readFile(file)
local output = {}
local f = io.open(file)
for each in f:lines() do
output[#output+1] = each
end
f:close()
return output
end
Now in C++, I tried to write that like this:
string * readFile(file) {
string line;
static string output[] = {};
ifstream stream(file);
while(getline(stream, line)) {
output[sizeof(output)+1] = line;
}
stream.close();
return output;
}
I know you can't return arrays from functions, only pointers. So I did this:
string *lines = readFile("stuff.txt");
And it threw me the error cannot convert 'std::string {aka std::basic_string<char>} to' std::string* {aka std::basic_string<char>*}' in intialization string *lines = readFile("stuff.txt");
Can anyone tell me what is wrong here, and is there a better way to read files into arrays?
EDIT:
I'm going to be using the returned array to do value matching using a for loop. In Lua this would be written as:
for _, each in ipairs(output) do
if each == (some condition here) then
--Do Something
end
end
How can this be done in C++, using vectors (according to the answer by Jerry Coffin)?
EDIT 2:
I can't match the vectors correctly for some reason. I wrote the code in a separate test file.
int main() {
vector<string> stuff = read_pass();
cout << stuff.size() << endl;
cout << stuff[0] << endl;
if (stuff[0] == "admin") {
cout << "true";
}
else {
cout << "false";
}
return 0;
}
read_pass() looks like this:
vector<string> read_pass() {
ifstream stream("stuff.txt");
string line;
vector<string> lines;
while(getline(stream, line)) {
lines.push_back(line);
}
stream.close();
return lines;
}
And stuff.txt looks like this:
admin
why?
ksfndj
I just put it some random lines to test the code. Every time I compile and run main.cpp the output I get is
3
admin
false
So why isn't the code being matched properly?
EDIT 3:
So instead of forcing myself down the vectors method of doing things, I decided to try this instead:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vector>
#include "basefunc.h"
using namespace std;
int main() {
string storedUsrnm;
string storedPw;
string pw = "admin";
string usrnm = "admin";
ifstream usernames("usrnm.accts");
ifstream passwords("usrpw.accts");
while(getline(usernames, storedUsrnm)) {
getline(passwords, storedPw);
print("StoredUsrnm " + storedUsrnm);
print("StoredPw: " + storedPw);
if (storedUsrnm == usrnm && storedPw == pw) {
print("True!");
return 0;
}
}
print("False!");
return 0;
}
Where print() is
void print(string str) {
cout << str << endl;
}
This still prints false, at the end, and it leads me to believe that for some reason, the "admin" read by the ifstream is different from the "admin" string. Any explanations for how this is so? Or does this code not work either?
Doesn't look to me like your current code should even compile. Anyway, I'd probably do something like this:
std::vector<std::string> read_file(std::istream &infile) {
std:string line;
std::vector<std::string> lines;
while (std::getline(infile, line))
lines.push_back(line);
return lines;
}
So the basic idea here is to read a line from the file, and if that succeeded, add that line (with push_back) to the vector of results. Repeat until reading a line from the file fails. Then return the vector of all the lines to the caller.
A few notes: especially at first, it's fairly safe to presume that any use of pointers is probably a mistake. That shouldn't be taken as an indication that pointers are terribly difficult to work with, or anything like that--just that they're almost never necessary for the kinds of things most relative beginners do in C++.
Likewise with arrays--at first, assume that what you might think of as an array in some other language translates to a std::vector in C++. C++ does also have arrays, but using them can wait a while (a long while, IMO--I've been writing C++ for decades now, and virtually never use raw pointers or arrays at all).
In the interest of simplicity, I've consolidated the data into the program, so it reads the data from the stringstream, like this:
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
vector<string> read_pass(istream &is) {
string line;
vector<string> lines;
while (getline(is, line)) {
lines.push_back(line);
}
return lines;
}
int main() {
istringstream input{ "admin\nwhy?\nksfndj" };
// To read from an external file, change the preceding line to:
// ifstream input{ "stuff.txt" };
vector<string> stuff = read_pass(input);
cout << stuff.size() << endl;
cout << stuff[0] << endl;
if (stuff[0] == "admin") {
cout << "true";
}
else {
cout << "false";
}
return 0;
}
At least for me, this produces:
3
admin
true
...indicating that it has worked as expected. I get the same with an external file. If you're not getting the same with an external file, my immediate guess would be that (at least the first line of) the file contains some data you're not expecting. If the problem continues, you might consider writing out the individual characters of the strings you read in numeric format, to give a more explicit idea of what you're really reading.
After a long time, I finally came up with the answer
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <map>
using namespace std;
typedef map<int, string> strArr;
strArr readFile(string file) {
ifstream stream(file);
string line;
strArr output;
while(getline(stream, line)) {
output[output.size()+1] = line;
}
stream.close();
return output;
}
It doesn't read the file into an array, but it does return a map that does basically the same thing

Reading a file into an array

I would like to read a text file and input its contents into an array. Then I would like to show the contents of the array in the command line.
My idea is to open the file using:
inFile.open("pigData.txt")
And then to get the contents of the file using:
inFile >> myarray [size]
And then show the contents using a for loop.
My problem is that the file I am trying to read contain words and I don't know how to get a whole word as an element in the array. Also, let's say that the words are divided by spaces, thus:
hello goodbye
Could be found on the file. I would like to read the whole line "hello goodbye" into an element of a parallel array. How can I do that?
Should be pretty straightforward.
std::vector<std::string> file_contents;
std::string line;
while ( std::getline(inFile,line) )
file_contents.push_back(line);
std::vector<std::string>::iterator it = file_contents.begin();
for(; it!=file_contents.end() ; ++it)
std::cout << *it << "\n";
Edit:
Your comment about having "hello goodbye" as element zero and element one is slightly confusing to me. The above code snip will read each line of the file and store that as an individual entry in the array 'file_contents'. If you want to read it and split it on spaces that is slightly different.
For context, you could have provided a link to your previous question, about storing two lists of words in different languages. There I provided an example of reading the contents of a text file into an array:
const int MaxWords = 100;
std::string piglatin[MaxWords];
int numWords = 0;
std::ifstream input("piglatin.txt");
std::string line;
while (std::getline(input, line) && numWords < MaxWords) {
piglatin[numWords] = line;
++numWords;
}
if (numWords == MaxWords) {
std::cerr << "Too many words" << std::endl;
}
You can't have one parallel array. For something to be parallel, there must be at least two. For parallel arrays of words, you could use a declarations like this:
std::string piglatin[MaxWords];
std::string english[MaxWords];
Then you have two options for filling the arrays from the file:
Read an entire line, and the split the line into two words based on where the first space is:
while (std::getline(input, line) && numWords < MaxWords) {
std::string::size_type space = line.find(' ');
if (space == std::string::npos)
std::cerr << "Only one word" << std::endl;
piglatin[numWords] = line.substr(0, space);
english[numWords] = line.substr(space + 1);
++numWords;
}
Read one word at a time, and assume that each line has exactly two words on it. The >> operator will read a word at a time automatically. (If each line doesn't have exactly two words, then you'll have problems. Try it out to see how things go wrong. Really. Getting experience with a bug when you know what the cause is will help you in the future when you don't know what the cause is.)
while (input && numWords < MaxWords) {
input >> piglatin[numWords];
input >> english[numWords];
++numWords;
}
Now, if you really one one array with two elements, then you need to define another data structure because an array can only have one "thing" in each element. Define something that can hold two strings at once:
struct word_pair {
std::string piglatin;
std::string english;
};
Then you'll have just one array:
word_pair words[MaxWords];
You can fill it like this:
while (std::getline(input, line) && numWords < MaxWords) {
std::string::size_type space = line.find(' ');
if (space == std::string::npos)
std::cerr << "Only one word" << std::endl;
words[numWords].piglatin = line.substr(0, space);
words[numWords].english = line.substr(space + 1);
++numWords;
}
Notice how the code indexes into the words array to find the next word_pair object, and then it uses the . operator to get to the piglatin or english field as necessary.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
// This will store each word (separated by a space)
vector<string> words;
// Temporary variable
string buff;
// Reads the data
fstream inFile("words.txt");
while(!inFile.eof())
{
inFile>>buff;
words.push_back(buff);
}
inFile.close();
// Display
for(size_t i=0;i<words.size();++i) cout<<words[i]<<" ";
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main ()
{
vector<string> fileLines;
string line;
ifstream inFile("pigData.txt");
if ( inFile.is_open() ) {
while ( !inFile.eof() ) {
getline(inFile, line);
fileLines.push_back(line);
}
inFile.close();
} else {
cerr << "Error opening file" << endl;
exit(1);
}
for (int i=0; i<fileLines.size(); ++i) {
cout << fileLines[i] << "\n";
}
cout << endl;
return 0;
}