Passing an array of pointers to a function - c++

This assignment is called a co-currence problem. The point of my program is to read in sentences from a file and ignore any punctuation. Then, I will read user input where the user will enter words separated by a space only and I have to search for those exact words in all the sentences of the file and return the line number where all the words are found.
My approach now is to create a pointer array to other arrays that contain the words for each sentence.
ifstream read;
string filename;
string **txtPtr = nullptr;
int numLines = 0;
getFileName();
getNumLines(read, fileName); //stores # of lines into numLines
txtPtr = new string*[numLines];
My question is, can I pass the pointer to a function as string *lines or string *lines[]?

I would parse the input file and build an index, and then I would look up user-entered words in that index. The index would be std::map with std::string as a key and with "Entry" struct as a value:
struct Entry {
int line;
int sentence;
};
typedef std::map<std::string, Entry> Index;
This is how insertion would look like:
Index index;
Entry val;
val.line = 1;
val.sentence = 2;
std::string word = "hi";
index.insert(Index::value_type(word, val));
This is how lookup would look like:
Index::iterator it = index.find(word);
if (it != index.end())
std::cout << "found:" << it->second.line;
I know it's not the answer for your question, but it might help anyway..

Related

How to initializing std::set<std::string> correctly?

Please help me, I have been trying to do this for the past two-three hours, all with no luck. I have a number of strings comming in form input.txt in the format
string1 string2
string3 string4
etc.
that I want to put into a std::set which is initially empty. I want to number the strings as they come in and put them into the set to keep track of the duplicates so I don't number them again. I am trying to initialize std::set<std::string> inGraph but can't make it work. I tried to initialize std::set<std::string> inGraph(0, tot_lines); where 0 to tot_lines is the range of the number of total strings I expect to get form the input. The I tried to initialize all with empty stirng like: std::set<std::string> inGraph(tot_lines, ""); and that failed. Here's what I have now:
struct StringInt {
std::string name; // associate name and number for each input string
int number;
};
int main(int argc, char* argv[]) {
int tot_lines = 100;
int icv1, icv2;
std::string vert1, vert2;
std::set<std::string> inGraph(); // this is the set I want to initialize
std::set<std::string>::iterator sit;
std::vector<StringInt> stringInts(tot_lines*2);
StringInt* si;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2 >> vert1 >> vert2) {
// read in input, put it in vars below
myfile2 >> vert1 >> vert2;
if (inGraph.find(vert1) != inGraph.end()) {
icv1 = i++;
si->name = vert1;
si->number = icv1;
inGraph.insert(vert1);
stringInts.push_back(*si);
}
else {
icv1 = si->number;
}
if (inGraph.find(vert2) != inGraph.end()) {
icv2 = i++;
si->name = vert1;
si->number = icv2;
inGraph.insert(vert2);
stringInts.push_back(*si);
}
else {
icv2 = si->number;
}
}
The error I get is: left of '.find' must have class/struct/union Can you please help me figure out how to initialize the std::set<std::string> inGraph so I can number the strings?
The error message is because you are a victim of Most Vexing Parse.
std::set<std::string> inGraph();
It is a function declaration whose return type is std::set<std::string>. Just remove the () after inGraph to make it a object declaration.

reading string text input to create a 2D Vector [closed]

Given a regular text file of:
56789
28385
43285
22354
34255
I am trying to read each string character in the text file and store them in a 2D vector.
First I would like to take each string row. Then I would like to take each character in the row and convert into an int then push_back into the row. Then I'd like to repeat for each line.
When outputting each column and row in my 2D vector, I would like the same exact output of:
56789 //each number an int now instead of a string
28385
43285
22354
34255
My issue is that I try to use i = stoi(j); which gives the error:
No matching function for call to 'stoi'
I do have the correct #include to be able to use stoi()
vector<vector<int>> read_file(const string &filename)
{
string file, line; stringstream convert; int int_convert, counter;
vector<vector<int>> dot_vector;
file = filename;
ifstream in_file;
in_file.open(file);
while (getline(in_file, line)) {
counter++; //how many lines in the file
}
char current_char;
while (getline(in_file, line)) {
for (int i = 0; i < counter; i++) {
vector<int> dot_row;
for (int j = 0; j < line.size(); j++) {
current_char = line[j];
i = stoi(j); //this is giving me an error
dot_row.push_back(i);
}
dot_vector.push_back(dot_row);
}
}
in_file.close();
return dot_vector;
}
Here
i = stoi(j);
// j is integer already
std::stoi
expect a string as the argument and what you providing is an int.
You can either convert the char to string and call std::stoi like follows
std::string CharString(1, line[j]);
dot_row.emplace_back(std::stoi(CharString));
or can convert the char to int directly while keeping to the vector:
dot_row.emplace_back(static_cast<int>(line[j] - '0'));
You have other problems in your code. Like in the comments mentioned, you do not need the extra counting of lines. Once you have your first while loop, you will reach already end of your file. The code after is then meaningless.
Secondly, you do not need two for loops. Simply use a range-based for loop for each line of strings and while iterating through it, convert to integer and save to vector.
while (getline(in_file, line))
{
std::vector<int> dot_row; dot_row.reserve(str.size());
for (const std::string& eachChar: line)
{
std::string CharString(1, eachChar);
dot_row.push_back(std::stoi(CharString));
// or other option mentioned above
}
dot_vector.push_back(dot_row);
}

Reading in words to a dynamically allocated array pointing to a struct

Im trying to take the string the user enters which will then clean it up a bit and store word by word in a dynamically allocated array. The array points to a struct. Im trying to store the array in the "English" struct variable.
Here is what i came up with but when i run the code my test cout of the array shows no output so i feel as the words are not being copied to the array.
//This program will take in a English sentence from the user where it will then store the string in an dynamically allocated array
//where it will be run through a functions which will convert the English sentence to pig Latin. The
//final sentence will be displayed to the screen for the user to see.
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
struct Word{
string english;
string piglatin;
};
Word * sentenceSetup(string, int &);
int main()
{
int Size=1;//default how many words in the sentence.
string userSent;
//ask the user for a sentence they want to convert
cout<<"Hello, please enter a string to convert to PigLatin: "<<endl;
getline(cin, userSent);
Word * arr = sentenceSetup(userSent,Size);
for (int i=0; i<Size;i++)
{
cout<< arr[i].english<<endl;
}
return 0;
}
//**************************************************************
//sentenceSetup Definition: In this function we will be asking *
//the user to enter in a string which then will be counted *
//for how many words it has and creating an array of structs *
//big enough to hold that string. The array will then be *
//returned to the calling function. NOTE: This function should *
//remove all capitalization and special characters except for *
//the end period, exclamation mark, or question mark. *
//**************************************************************
Word * sentenceSetup(string userSent, int &size)
{
char nextCharacter;
//check the input for any unwanted special characters not listed in the function def.
for( int i=0; i<int(userSent.size()); i++)
{
nextCharacter = userSent.at(i);
if(ispunct(userSent[i]))
{
userSent.erase(i--, 1);
}
}
//change the whole sentence to lower case
for (int i=0; i<int(userSent.size()); i++)
{
userSent[i]=tolower(userSent[i]);
}
//Check each character in the string to see if it is a space. If the loop
//notices a space then a space equals a word in the string.
for (int i =0; i<int(userSent.size());i++)
{
nextCharacter = userSent.at(i); //Reads the character
if(isspace(userSent[i]))
{
size++;
}
}
//test to see if num count works
cout<<"There is "<<size << " words in the sentence."<<endl;
//test to see if special characters removed
//cout<<userSent<<endl;
//allocate an array to store the words in for the struct Word
Word *temp= new Word[size];
int count =0;
string word;
for(count=0;count<size;count++)
{
if(isspace(userSent[count]))
{
word =userSent.substr(0,count);
temp[count].english=word;
userSent.erase(0,count);
}
}
//test
for(count =0; count<size;count++)
{
cout<<temp[count].english;
}
return temp;
}
The problem lies in how you are trying to write the data into dynamically allocated Word* temp variable as well as the for loop you are using for the same. The above code needs to be rectified as follows
I'm creating a main array of strings which will be returned by the function
//allocate an array to store the words in for the struct Word
Word *main= new Word[size];
int count =0;
string word;
//Variable to keep track of start of each word
int start_count =0;
//Create a temp pointer to iterate through the memory allocated for each word
Word *temp = main;
//Variable to keep track of length of each word
int bytes_to_cpy =0;
The for loop should loop until the length of the sentence, not just the number of words
for(count=0;count<=userSent.length();count++){
if(isspace(userSent[count]))
{
//Copy the required bytes as a substring
word =userSent.substr(start_count,bytes_to_cpy);
//Copy the word to the main array
temp->english=word;
//Increment the pointer;
temp++;
//Ignore the white space for the next word
start_count=count+1;
//Reset the length of the word
bytes_to_cpy=0;
}
//Count the length of the word
bytes_to_cpy++;
}
//Add the last word to the array
word =userSent.substr(start_count,userSent.length()-start_count);
temp->english = word;
temp = main;
//test
for(count =0; count<size;count++)
{
cout<<temp->english<<endl;
temp++;
}
return main;
Apart from this, you also need to take care of multiple whitespaces, whitespace at the beginning and at the end of the user input string in our Function that calculates the number of words. Your current implementation does not take care of these.

C++ How do we find each line of a word from a text file?

I am reading a text file word for word and I am trying to find the number of the line the word is in. For example if I had the following:
Dog Cat
Car
Truck
Dog is found on line one, Cat is found on line one, Car line two, and Truck line 3.
I have the following code:
int main(){
string word;
ifstream inFile;
Node* rootPtr = NULL; // Pointer to the root node
inFile.open("example.txt");
if (!inFile)
cout << "Unable to open text file";
while (inFile >> word) {
if (word == "#")
break;
/* THIS DOES NOT WORK! Most likely because my text file doesn't contain /n but this is the
kind of logic I am looking for
else if (word == "/n"){
counter++;
cout << counter;
}
*/
else{
rootPtr = Insert(rootPtr,word.substr(0,10));
}
}
inOrderPrint(rootPtr);
inFile.close();
}
You can ignore anything to do with the pointers. That is for some other stuff. I have tried figuring out how to check for the end of the line and creating a counter that will increment every time then end of a line is found but I was unsuccessful.
Thanks for your help!
You always can use carefully getline (http://www.cplusplus.com/reference/string/string/getline/) and count the lines by yourself. Of course, split the read string into words from the line: Split a string in C++?
You could try a few things. A map would work:
#include <map>
map <int,string> words;
And then while adding words:
int wordNum = 0;
while (inFile >> word) {
if (word == "#")
break;
else{
words[wordNum] = word;
}
And recall your words like this:
int x = 0;
while ( x < map.size() ) {
cout << words[x] << " ";
x++;
}
Another option is to store your words into a struct containing two strings, or any two
corresponding data types (one for your word, one for the #):
struct words_struct{
string words;
string wordNum;
} store ; // single instance
And store the words like this:
int x=0;
while (inFile >> word) {
if (word == "#")
break;
else{
store->words.append(word);
store->wordNum.append(x);
x++;
}
The above code needs some fixin' (no space between words, int->string, etc) but the gist is correct. Hope this helps! Best of luck!
There is an issue with your requirements: What if the word exists on multiple lines?
For the simple case, I recommend keeping a line counter and using std::map<string, unsigned int>, where string is your word and unsigned int is the line number it first occurs on.
To handle all line numbers that a word occurs on, you may want to use std::map<string, std::vector<unsigned int> >, where the std::vector contains all the line number that the word occurs on.
Example:
typedef std::map<std::string, unsigned int> Word_Ref_Container;
Word_Ref_Container word_line_reference;
//...
std::string text_line;
unsigned int line_number = 1;
while (getline(input_file, text_line)
{
std::istringstream text_stream(text_line);
std::string word;
while (text_stream >> word)
{
if (word_line_reference.find(word) != word_line_reference.end())
{
word_line_reference[word] = line_number;
}
}
++line_number;
}
You can use the getline function
string line;
int lineNum = 0; // Or 1
while(getline(infile, line))
{
i++;
}
And you can use the stringstream if you want to split the line by word.
#include <sstream>
// Your code
while(getline(infile, line))
{
stringstream ssLine(line);
string substr;
while(ssLine)
{
ssLine >> substr;
// substr will now hold each word (words should be separated by spaces)
}
i++;
}
Or better, I have my version of split and you are welcome to use that
/**
* Equivalent to java's string.split() function.
*
* #param toPopulate The return value of this function.
* #param s The string we want to split.
* #param delim The delim which we want to split. This will not be included in
* the splitted string. User should pass only one character to this
* string.
*/
void split(vector<string> &toPopulate, string s, string delim)
{
// Will hold the start of the substring (after the delim). Initially the
// substring will start at 0.
int substrStart = 0;
while (substrStart < s.length())
{
// Will hold the position of the delim.
int curFoundPos = s.find(delim, substrStart);
// Holds the current substring.
string oneOfSplittedStr;
// The delim not found. So, take the substring from previous delim to end.
if (curFoundPos == -1)
{
oneOfSplittedStr =
s.substr(substrStart, s.length() - substrStart);
// To break off the loop. If not for this stmt, we will go into infinite loop.
substrStart = s.length();
}
else
{
oneOfSplittedStr =
s.substr(substrStart, curFoundPos - substrStart);
// our next substring will start one greater than the current found position.
substrStart = curFoundPos + 1;
}
// Empty - Nah
if (!oneOfSplittedStr.empty() && oneOfSplittedStr.compare("") != 0)
toPopulate.push_back(oneOfSplittedStr);
}
}
And you can always use boost's split
I found a nice way to go about doing this. All I had to do is add the following:
char c;
while (inFile >> word) {
c = inFile.get();
else if (c=='\n'){
rootPtr = Insert(rootPtr,word.substr(0,10));
counter++;
}
}
Thanks for all of your suggestions!

How to number input in ascending order?

I have input coming in form a file input.txt as two columns of strings such as:
string1 string2
string3 string4
etc.
I am trying to number the strings in ascending order starting form 0 but in such a way that repeating strings don't get assigned new values but keep the once already assigned to them.
I decided to use a set::find operation to do this, but I am having a hard time making it work. Here's what I have so far:
int main(int argc, char* argv[]) {
std::ifstream myfile ("input.txt");
std::string line;
int num = 0; // num is the total number of input strings
if (myfile.is_open()) {
while(std::getline(myfile, line)) {
++num;
}
}
std::string str1, str1; // strings form input
int str1Num, str2Num; // numbers assigned to strings
int i = 0; // used to assign values to strings
StringInt si;
std::vector<StringInt> saveStringInts(num);
std::set<std::string> alreadyCounted(num, 0);
std::set<std::string>::iterator sit;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2.good()) {
// read in input, put it in vars below
myfile2 >> str1 >> str2;
// if strings are not already assigned numbers, assign them
if ((*(sit = alreadyCounted.find(str1)).compare(str1) != 0) { // doesn't work
str1Num = i++;
alreadyCounted.insert(str1);
saveStringInts.push_back(StringInt(str1Num));
}
else {
str1Num = si->getNum(str1);
}
if ((*(sit = alreadyCounted.find(str2)).compare(str2) != 0) {
str2Num = i++;
alreadyCounted.insert(str2);
saveStringInts.push_back(StringInt(str2Num));
}
else {
str2Num = si->getNum(str2);
}
// use str1 and str2 in the functions below before the next iteration
}
}
Unfortunately, I tried other approaches and now completely stuck. If you know how to fix my code or can suggest a better way to accomplish my task, I would greatly appreciate your help.
You need to compare std::set<int>::iterator against the end() iterator of your set, rather than dereferencing the iterator and comparing its value against something! Actually, derferencing the end() iterator is undefined behavior:
if ((*(sit = alreadyCounted.find(str1)).compare(str1) != 0) // WRONG: don't do that!
should really be
if (alreadyCounted.find(str1) != alreadyCounted.end())
... and likewise for the other string. Personally, I would use a different technique, though: when insert()ing into a std::set<T>, you get back a pair of an iterator and an indicator whether the object was inserted. The latter together with the current set's size give the next value, e.g.:
bool result = alreadyCounted.insert(str1).second;
strNum1 = result? alreadyCounted.size() - 1: si->getNum(str1);

Resources