C++ While loop scoping issue - c++

I recently started learning C++, and I'm currently trying build a tool that I recently built in python. My issue is that I can't figure out how I can make the length of the list of words global for example:
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, char* argv[]) {
....
fstream file;
file.open(argv[i], ios::in);
if (file.is_open()) {
string tp;
while (getline(file, tp)) {
// cout << tp << "\n" << endl;
string words[] = {tp};
int word_count = sizeof(words) / sizeof(words[0]);
for (int e = 0; e < word_count; e++) {
cout << word_count[e];
}
}
file.close();
} else {
cout << "E: File / Directory " << argv[i] << " Does not exist";
return 0;
}
....
}
Where it says int word_count = sizeof(words) / sizeof(words[0]); and tring words[] = {tp};, I want to be able to use that globaly so that I can then later use then length of the array and the array itself later on so that I can loop through it and use them in another statement.
Can someone tell me how to do so?
And by the way, I've only been doing C++ for about 4 days so please dont get annoyed if I don't understand what you tell me.

string words[] = {tp}; creates an array with one element in it, the whole line. The name words implies that you instead want to store the individual words. The scope is also wrong if you want to use it after the loop is done. You need to declare it before the loop. Use a std::vector<std::string> to store the words. It could look like this:
#include <vector>
int main(int argc, char* argv[]) {
std::vector<std::string> words;
// ...
if (file) {
std::string word;
while (file >> word) { // read one word
words.push_back(word); // store it in the vector<string>
}
std::cout << "word count: " << words.size() << '\n';
// print all the words in the vector:
for(std::string& word : words) {
std::cout << word << '\n';
}
// file.close() // not needed, it'll close automatically when it goes out of scope
}

You have a number of issues here. First, rather than adding tp to an array called words on each iteration of the loop, you're actually creating a new array words and assigning tp to the first element of the array. words is destroyed at the end of each iteration of the while loop, but even if it wasn't, the {tp} expression would overwrite whatever was already in the array. Your line to calculate the length will therefore always calculate the length as 1.
Next, word_count[e] is trying to read element e of an array called word_count, but word_count is actually an integer. I'd be surprised if this actually compiled.
First I'd recommend you use a std::vector, and then if you just increase its scope to outside the loop then you can use it elsewhere. For example:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
int main(int argc, char* argv[]) {
fstream file;
file.open(argv[i], ios::in);
std::vector<std::string> words;
if (file.is_open()) {
string tp;
while (getline(file, tp)) {
words.push_back(tp); // Add tp to the vector of words
}
int word_count = words.size(); // vectors 'know' their own size.
for (int e = 0; e < word_count; e++) {
cout << words[e];
}
file.close();
} else {
cout << "E: File / Directory " << argv[i] << " Does not exist";
return 0;
}
}
// You can now use words as you please, because it is still in scope.
}

Related

unique words from a file c++

ts been 3 days i just cant identify whats wrong with the program the program should compare words by words instead it only comparing a character to charcter its is showing like if i have words like (aaa bbb cc dd ) the result its printing is a b and same is the sentence file if i put paragraphs to compare its only comparing few character please help me
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream myfile("unique.text");
int count = 0;
string temp;
string a;
int i,j;
while(getline(myfile,temp))
{
for(i=0 ; i < sizeof(temp); i++)
{
for(int j = 0; j < i; j++)
{
if (temp[i] == temp[j])
break;
}
if (i == j)
cout << temp [i] <<" , ";
}
myfile.close ();
}
You have a couple of problems
temp is of type string. sizeof is not the way to determine the length of a string (it's used for determining things like the number of bytes in an int). You want:
temp.length()
Secondly, indexing into a string (temp[n]) gives you the nth character, not the nth word.
You can make getline split into words by adding a third delimiter parameter:
getline (myfile, temp, ' '))
So, some bugs in your code.
Mixing up characters and strings, closing the file in the while loop and not storing last words.
One recommenadtion. Before you write code, write comments for what you want to do.
Meaning, make a design, before you start coding. That is very important.
For your problem at hand in the title of this thread:
unique words from a file c++
I prepared 3 different solutions. The first is just using very simple constructs. The second is using a std::vector. And, the 3rd is the C++ solution using the C++ algorithm library.
Please see:
Simple, but lengthy
And not recommended, because we should not use raw pointers for owned memory and should not use new
#include <iostream>
#include <fstream>
#include <string>
const std::string fileName{ "unique.text" };
unsigned int numberOfWords() {
// Here we will count the number of words in the file
unsigned int counter = 0;
// Open the file. File must not be already open
std::ifstream sourceFileStream(fileName);
// Check, if we could open the file
if (sourceFileStream) {
// Simply read all words and increment the counter
std::string temp;
while (sourceFileStream >> temp) ++counter;
}
else {
// In case of problem
std::cerr << "\nCould not open file '" << fileName << "'\n";
}
return counter;
}
int main() {
// Get the number of words in the source file
unsigned size = numberOfWords();
// Allocate a dynamic array of strings. Size is the count of the words in the file
// Including doubles. So we will waste a little bit of space
std::string* words = new std::string[size+1];
// Open the source file
std::ifstream sourceFileStream(fileName);
// Check, if it could be opened
if (sourceFileStream) {
// We will read first into a temporary variable
std::string temp;
// Her we will count number of the unique words
unsigned int wordCounter = 0;
// Read all words in the file
while (sourceFileStream >> temp) {
// We will search, if we have read alread the word before. We assume NO for the beginning
bool wordIsAlreadyPresent = false;
// Go through all alread read words, and check, if the just read word is already existing
for (unsigned int i = 0; i < wordCounter; ++i) {
// Check, if just read word is already in the word array
if (temp == words[i]) {
// Yes it is, set flag, and stop the loop.
wordIsAlreadyPresent = true;
break;
}
}
// if the word was not already there
if (! wordIsAlreadyPresent) {
// Then add the just read temporary word into our array
words[wordCounter] = temp;
// And increment the counter
++wordCounter;
}
}
// Show all read unique words
for (unsigned int i = 0; i < wordCounter; ++i) {
std::cout << words[i] << "\n";
}
}
else { // In case of error
std::cerr << "\nCould not open file '" << fileName << "'\n";
}
delete[] words;
}
Using a vector. Already more compact and better readable
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
const std::string fileName{ "unique.text" };
int main() {
// Open the source file
std::ifstream sourceFileStream(fileName);
// Check, if the source file is oepen
if (sourceFileStream) {
// Temporary string for holding just read words
std::string temp;
// In this vector we will store all unique words
std::vector<std::string> words;
// Read all words from the source file
while (sourceFileStream >> temp) {
// We will search, if we have read alread the word before. We assume NO for the beginning
bool wordIsAlreadyPresent = false;
// Go through all alread read words, and check, if the just read word is already existing
for (unsigned int i = 0; i < words.size(); ++i) {
// Check, if just read word is already in the word vector
if (temp == words[i]) {
// Yes it is, set flag, and stop the loop.
wordIsAlreadyPresent = true;
break;
}
}
// if the word was not already there
if (not wordIsAlreadyPresent) {
// Then add the just read temporary word into our array
words.push_back(temp);
}
}
for (unsigned int i = 0; i < words.size(); ++i) {
std::cout << words[i] << "\n";
}
}
else {
std::cerr << "\nCould not open file '" << fileName << "'\n";
}
}
And 3., more advance C++ programming. Just very few lines and elegant code.
But too difficult to understand for starters.
#include <iostream>
#include <fstream>
#include <set>
#include <string>
#include <iterator>
#include <algorithm>
const std::string fileName{ "unique.text" };
int main() {
// Open the source file and check, if it could be opend and there is no failure
if (std::ifstream sourceFileStream(fileName); sourceFileStream) {
// Read all words (everything delimited by a white space) into a set
std::set words(std::istream_iterator<std::string>(sourceFileStream), {});
// Now we have a set with all unique words. Show this on the screen
std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
// If we could not open the source file
else {
std::cerr << "\nCould not open file '" << fileName << "'\n";
}
return 0;
}

C++ Read every second line from a text file and safe in vector

I am doing a small "translator" project. I have this text file which looks like this:
friend
amigo
mother
madre
telephone
teléfono
As you can see the first line is the English word and the following is the word to translate. I want to save every English word in a vector and every Spanish word into another vector. My problem is that I don't know exactly how to skip these lines.
The vector is not your best choice. Use std::map which is really good for a dictionary because elements in it are ordered and unique which is really good choice rather than using two vectors separating words from their definitions:
#include <map>
#include <iostream>
#include <string>
int main()
{
std::map<std::string, std::string> content;
std::ifstream in("Data.txt");
std::string word, definition;
while (std::getline(in, word) && std::getline(in, definition))
content[word] = definition;
for (const auto& p : content)
std::cout << p.first << " : "
<< p.second << std::endl;
std::cout << std::endl;
// To search for a word in dictionary:
std::string search = "mother";
auto ret = content.find(search);
if (ret != content.cend())
std::cout << search << " : \n" <<
ret->second << std::endl;
else
std::cout << search << " : was not found in dictionary!" << std::endl;
std::cout << "\n";
}
I do not know what your intentions are or what you have tried. But I hope this helps.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(){
string array[10] = {"friend", "amigo", "mother", "madre", "telephone", "teléfono"};
vector<string> englishWords;
vector<string> spanishWords;
for(int i = 0; i < 10; i+=2){
englishWords.push_back(array[i]);
}
for(int i = 1; i < 10; i+=2){
spanishWords.push_back(array[i]);
}
for(int i = 0; i < englishWords.size(); i++){
cout << englishWords[i] << endl;
}
for(int i = 0; i < spanishWords.size(); i++){
cout << spanishWords[i] << endl;
}
}
My problem is that I don't know exactly how to skip these lines.
Here's an idea: assuming that you always have an English and a Spanish word(or line) occurring alternatively in the file, you can keep a boolean variable is_english to denote the language of the current word(or line). It's set to true initially, assuming that the first word(or line) is in English, and then set to false, true, false, etc. alternatively, each time you read a new word(or line). In every iteration, if is_english is true, you append the current word(or line) to English vector. Otherwise, append to Spanish vector. The code is given below for your reference.
Assuming you have a file like this:
friend
amigo
mother
madre
telephone
teléfono
#include <iostream>
#include <fstream>
#include <vector>
int main(){
std::ifstream in("./input.txt");
if(!in){
std::cout<<"Error opening the file! Maybe the file does not exist!";
return 0;
}
std::string one_line;
std::vector<std::string> english, spanish;
bool is_english = true; // Variable to denote current word (starts with english)
while(in.eof()==0){ // Read until end of file
getline(in,one_line);
if(is_english) // If english
english.push_back(one_line);
else // Otherwise, spanish
spanish.push_back(one_line);
is_english = !is_english; // Flip boolean variable value
}
std::cout<<"English words:\n";
for(const auto i:english)
std::cout<<i<<std::endl;
std::cout<<"\nSpanish words:\n";
for(const auto i:spanish)
std::cout<<i<<std::endl;
return 0;
}
Output
English words:
friend
mother
telephone
Spanish words:
amigo
madre
teléfono

reading row from text file into two vectors c++

I am trying to read the two words "kelly 1000" in the text file "players", into vectors players and balances respectively. Don't know why it's not working?
string name = "kelly";
int main()
{
int num =0;
vector<string> players;
vector<int> balances;
ifstream input_file("players.txt");
while(!input_file.eof())
{
input_file >> players[num];
input_file >> balances[num];
num++;
}
for(size_t i = 0; i=players.size(); i++)
{
if(name==players[i])
cout << "Welcome " << name << ", your current balance is " << balances[i] << "$." << endl;
else
break;
}
With operator[] you can only access existing elements. Going out of bounds invokes undefined behaviour. Your vectors are empty and you need to use push_back method to add elements to them.
Second problem is while (!file.eof()) anti-pattern. It'll typicaly loop one to many times because the read of last record doesn't neccesarily trigger eof. When reading from streams, always check whether input succeeded before you make use of values read. That's typicaly done by using operator>> inside loop condition.
string temp_s;
int temp_i;
while (input_file >> temp_s >> temp_i) {
players.push_back(temp_s);
balances.push_back(temp_i);
}
This way the loop stops if operator>> fails.
//Hope this is something you want dear.Enjoy
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
string name = "kelly";
int main()
{
int num =0;
string tempname;
int tempbalance;
vector<string> players;
vector<int> balances;
ifstream input_file("players.txt");
while(!input_file.eof())
{ input_file>>tempname;
input_file>>tempbalance;
players.push_back(tempname);
balances.push_back(tempbalance);
}
for(size_t i = 0; i<players.size(); i++)
{
if(name==players.at(i))
cout<< "Welcome " << name << ", your current balance is " << balances.at(i)<< "$." << endl;
}
return 0;
}

C++: Counting function only counts first line

I have a problem trying to count words inside of a vector. A vector holds every line from a file as an object. v[0] is the first line, v[1] is the second line, so on.
For my countWords() function, it only works for counting v[0]. Any object past that is ignored or missed some how. Any ideas? Thanks in advance.
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int countWords(vector<string> v)
{
stringstream ss;
string word;
int count = 0;
for(int i = 0; i < v.size(); i++) {
ss.str(v[i]);
while (ss >> word)
count++;
}
return count;
}
void readFile(string filename,vector<string> &v)
{
fstream file;
string line;
file.open(filename,ios::in);
while(getline(file,line)) { //Reads the file line by line ...
if(line == "") //... ignoring any empty lines ...
continue;
v.push_back(line); //... and puts them into our vector.
}
file.close();
}
int main(int argc,char* argv[])
{
if (argc != 2) { //Terminate unless the user enters -ONE- entry.
cout << "Usage: " << argv[0] << " <filename>" << endl;
exit(1);
}
string filename = argv[1];
vector<string> fileContents;
readFile(filename,fileContents);
cout << countWords(fileContents) << endl;
}
As an alternative to RichieHindle's answer, this works too. Just have the stringstream scope local to the for loop and it will reset properly.
int countWords(vector<string> v)
{
string word;
int count = 0;
for(int i = 0; i < v.size(); i++) {
stringstream ss(v[i]);
while (ss >> word)
count++;
}
return count;
}
Before you reuse stringstream you must do
ss.clear();
after your while loop.
You could also declare it inside the for() loop, but then it would be reinitialized again. For readabillity, this might be better. Performancewise it could make a difference.
I bet ss goes into an error state when you've exhausted it for the first time and doesn't reset just because you call str.
Declare ss inside the for loop and pass the string directly to the constructor. This avoids such problems.
In general, you have the bad habit of declaring your variables in a bunch instead of closest to where you need them, and not using constructors. For example, you could pass the filename to fstream's constructor instead of calling open. And you could use ifstream so you don't need the second argument.

How to read space and newline separated integers into a 2D array in C++?

I have a .txt file of numbers (in this case all less than 100) separated by spaces, in rows separated by new lines. Something like this:
41 53 07 91 44
52 17 13 03 21
I would like to read these numbers into a 2d array, exactly as they appear, so that spaces separate columns of the array, and new lines separate rows.
I can get it to read the lines in as strings, but then I'm having trouble separating out individual numbers, and getting it to treat them as integers.
Try this:
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
int main()
{
// The result of the read is placed in here
// In C++, we use a vector like an array but vectors can dynamically grow
// as required when we get more data.
std::vector<std::vector<int> > data;
// Replace 'Plop' with your file name.
std::ifstream file("Plop");
std::string line;
// Read one line at a time into the variable line:
while(std::getline(file, line))
{
std::vector<int> lineData;
std::stringstream lineStream(line);
int value;
// Read an integer at a time from the line
while(lineStream >> value)
{
// Add the integers from a line to a 1D array (vector)
lineData.push_back(value);
}
// When all the integers have been read, add the 1D array
// into a 2D array (as one line in the 2D array)
data.push_back(lineData);
}
}
Ok, the "exactly as they appear" requirement means that you need a ragged array, in case different number of columns appear in different rows. I would use std::vector< std::vector<long> >. Each contained vector corresponds to one row.
So, each time you read a row of text, create a new empty vector.
Call strtol repeatedly on the row you read, using push_back to collect them into the vector. When the output pointer is the same as the input pointer (this indicates failure, probably because you reached the end of the line), push_back the whole vector and start the next row.
Something like this:
std::vector< std::vector<long> > all_data;
std::string text_row;
while(getline(fin, text_row)) {
all_data.push_back();
std::vector<long>& this_row = *all_data.rend();
const char* p1 = text_row.c_str();
const char* p2;
while (1) {
long num = strtol(p2 = p1, &p1, 0);
if (p1 == p2) break;
this_row.push_back(num);
}
/* to ignore blank lines, add this code
if (this_row.empty()) all_data.pop_back();
*/
}
The following code shows how to solve your problem. It also shows how you can use RAII when opening a file. This is good practice when acquiring resources. By acquiring the resource in the constructor and releasing it in the destructor a resource leak can be prevented if an exeption is thrown. This is considered good practice in the C++ world.
#include <fstream>
#include <vector>
struct FileHandle
{
std::ifstream file_;
FileHandle(std::string file_name)
{
file_.open(file_name);
}
~FileHandle()
{
file_.close();
}
};
bool next_char_is_end_of_line(std::ifstream &file)
{
bool found = false;
char c;
file.get(c);
if(c == '\n')
found = true;
file.unget();
return found;
}
int main()
{
FileHandle fh("c:\\your_file.txt");
std::vector<std::vector<int> > v;
std::vector<int> current_line;
int x;
while(fh.file_ >> x)
{
current_line.push_back(x);
if(next_char_is_end_of_line(fh.file_))
{
v.push_back(current_line);
current_line.clear();
}
}
//Then just access each std::vector<std::vector<int>>
//v[0].size(); ...
return 0;
}
This code gives you a "jagged" vector by putting all numbers on each row into a separate vector. This vector is then added to main vector. So each line in the "vector" can have different lengths. I think you get the idea...
Good luck!
Try stringstream.
There are actually two problems, here:
how to recognize the input and ...
how to represent it in memory.
You spoke about "2d Array": is this a requirement or just an hypothesis? is the 2x5 size a requirement or just a sample?
You spoke about a file layout. It that mandatory, or not? Must you admit (and check) eventually misleading like more numbers, or misalignment? What do you have to do in case the row have 6 numbers the first and 4 the second?
A very simple solution can be use a 2x5 array and fill it up with a guarded loop:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdexcept>
const int ROWS=2;
const int COLS=5;
int main(int argc, char** argv)
{
int m[ROWS][COLS];
try
{
std::ifstream s(argv[1]);
if(!s) throw std::runtime_error("cannot open file");
for(int r=0; r<ROWS; ++r)
for(int c=0; c<COLS; ++c)
if(!(s >>m[r][c])
throw std::runtime_error("insufficient or bad input");
}
catch(const std::exception& e)
{
std::cout << "Reading error: " << e.what() << std::endl;
return -1;
}
std::cout << "read matrix is \n";
for(int r=0; r<ROWS; ++r)
{
for(int c=0; c<COLS; ++c)
std::cout << std::setw(8) << m[r][c];
std::cout << '\n';
}
std::cout << std::endl;
return 0;
}
This will read 10 numbers separated by "blanks", no matter how distributed and aligned.
(it is assumend the the 2x5 is a consrain.)
On the other end, you may have to detect yourself how wide the matrix is: the file can have any number of lines, each whatever number of elements long.
In this case you need a "flexible structure", and you need to identify lines and elements in the lines.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <stdexcept>
#include <utility>
int main(int argc, char** argv)
{
std::vector<std::vector<int> > m;
try
{
std::ifstream fs(argv[1]);
if(!fs) throw std::runtime_error("cannot open input file");
while(fs)
{
std::string line;
std::getline(fs,line);
std::vector<int> row;
std::stringstream ss(line);
int x;
while(ss >> x)
row.push_back(x);
if(!row.empty())
m.emplace_back(std::move(row));
}
}
catch(const std::exception& e)
{
std::cout << "Reading error: " << e.what() << std::endl;
return -1;
}
std::cout << "read lines: \n";
for(auto i=m.begin(); i!=m.end(); ++i)
{
for(auto j=i->begin(); j!=i->end(); ++j)
std::cout << std::setw(8) << *j;
std::cout << '\n';
}
std::cout << std::endl;
return 0;
}