I am currently trying to do an assignment in my C++ class. I think I've gotten as far as I can on my own.
The assignment calls for me to read names from a text file, put the names in order, and write them to a new file.
The names in the given text file are formatted as follows:
Jackie Sam Tom Bill Mary Paul Zev Barb John
My code that I have for the assignment:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string nameArray[26] = {};
int main() {
// Defined variables
int y = 65, lineNumber = 0;
string readName;
// Opens the text file LineUp
ifstream readfile;
readfile.open("LineUp.txt");
// Opens the text file InOrder
ofstream outfile;
outfile.open("InOrder.txt");
// Read name from file
while (readfile >> readName) {
// If first character of name != char(y),
// run loop
while (readName[0] != char(y)) {
y++;
lineNumber++;
// If first character of name = char(y),
// add name to lineNumber's position in array
if (readName[0] == char(y)) {
nameArray[lineNumber] = readName;
}
}
// Reset values of lineNumber and y
y = 65;
lineNumber = 0;
}
// Writes names in array to file
for (int x = 0; x <= 25; x++)
outfile << nameArray[x] << endl;
// Close files
readfile.close();
outfile.close();
// Print statement so you can see at least
// something happens
cout << "KIDS ORGANIZED. BEEP. BOOP. BEEP.\n";
cin.get();
return 0;
}
The output to the file made in the program that holds the ordered names:
Barb
John
Mary
Paul
Sam
Tom
Zev
(It contains many more empty lines than what stack overflow shows.)
The questions I have are as follows:
1: How can I get rid of the empty spaces in the array for when I want to write the names to the text file InOrder.txt?
2: The names Jackie and Bill are not being shows because the positions in the array are being overwritten by the other two names. How can I check if those positions are filled to add in these two names?
3: Is there anything in my program I can do to make it more efficient or more readable? Or just do better in general, I guess.
A big thank you to anyone willing to try to figure out how to solve these problems!
One problem is the length of your string array. You only reserve 26 elements. In your for loop
for (int x = 0; x <= 26; x++)
outfile << nameArray[x] << endl;
you try to access element 0 to 26 (that are 27 elements in total), that's more than you allocated. You have to change this loop to something like
for (int x = 0; x < 26; x++)
outfile << nameArray[x] << endl;
so it won't crash.
But the main problem in your program is the sorting system. As you already have noticed, as soon as two kids have a name with the same first letter, one name will be overwritten.
A better approach would be to use some of the standard libraries.
First i would not use a std::string array, but a std::vector. A vector is, simply said, a better and dynamic array with a lot more functions. If you want to know more about vectors, check here.
With a vector you can read every name first and sort it later:
std::vector<std::string> nameVector;
while(readfile >> readName) {
//add name to the end of the vector
nameVector.push_back(readName);
}
Now you can use std::sort() to sort the vector.
std::sort(nameVector.begin(), nameVector.end());
And last write everything to your output file:
for(int x = 0; x < nameVector.size(); ++x)
outfile << nameVector[x] << std::endl;
Firstly don't use the nameArray[26] in the for-loop as it is out of range.
Secondly by now surely you must have realized the issue. You logic reserve a place for name with each alhpabet and if there are two names starting with same alphabet it creates problem. Also the blank spaces are ther because of the places in the array not having any names starting with respective alphabet. for example. no name starts with A so a blank space will be in start of the file similary no name starts with C so a blank space would be there after the name Barb.
As per your logic the solution should be something like this:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Can be many names
string nameArray[100] = {};
int main() {
// Defined variables
int y = 65, lineNumber = 0;
string readName;
// Opens the text file LineUp
ifstream readfile;
readfile.open("LineUp.txt");
// Opens the text file InOrder
ofstream outfile;
outfile.open("InOrder.txt");
//Run while the names are not checked for all alphabets
while (y <= 90) {
// Read names from file till the end is reached
while (readfile >> readName) {
// If first character of name != char(y),
// run loop
if(readName[0] == char(y)) {
// If first character of name = char(y),
// add name to lineNumber's position in array
nameArray[lineNumber] = readName;
lineNumber++;
}
}
}
//Check File for next character
y++;
}
// Writes names in array to file
for (int x = 0; x < lineNumber; x++)
outfile << nameArray[x] << endl;
// Close files
readfile.close();
outfile.close();
// Print statement so you can see at least
// something happens
cout << "KIDS ORGANIZED. BEEP. BOOP. BEEP.\n";
cin.get();
return 0;
}
Note that this code works for only Upper-case letters and sorts only based on first alphabet.
Related
I am currently working in c++. My goal is to be able to implement the A* algorithm, through a series of steps I plan to be able to store, and list the shortest path from Point A to Point B.
My program will take in a command-line argument which will be a file to read in. The purpose of giving the program a file t read in is that each file is its own "grid" for A* to process. The format for the grid file is as follows:
Num 1 = WIDTH and then Num 2 = HEIGHT
o = start
* = finish
. = passable node
# = impassable wall
So an example grid, let's call it "Grid1", would be:
8 5
o......#
......##
.....###
....####
.......*
So to process this, when you run the program you would pass in "Grid1".
From here, I plan to create a filestream and read in each character into a 2D Array/Vector to attempt to recreate the grid so I can have positions. For example, in "Grid1" the character "o" would be at position [0][0] in a Array/Vector. My attempted implementation is as follows:
#include <iostream>
#include <fstream>
#include <vector>
#include <unordered_map>
using namespace std;
int main(int argc, char *argv[])
{
//Checking for CLA
if( argc < 1 )
{
cout << "Program must take atleast <1> arguments!" << endl << "1) File to read 2) OPTIONAL: Type of search" << endl;
}
//CLA #1 is the file containing GRID to be read in
string gridTBO = argv[1];
//Start new filestream and open grid file for reading
ofstream gridFile;
gridFile.open( gridTBO );
//Check to make sure we can even open the file before proceeding
if( gridFile.is_open() )
{
//To store grid data
vector<string> grid;
grid[0][0].push_back("Hello");
cout << grid[0][0];
}
However, in regards to my "push_back", it seems that I am not able to print out or throw the data into the vector. I would primarily want each index such as [0][0] [0][1] [0][2] to hold each individual character in order to recreate a grid like the one I pass in.
I am not quite sure what I am doing incorrectly as I am fairly inexperienced using vectors, however, the code for pushing data into the grid is purely for testing purposes. Once I am able to parse some data into the vector, I would then like to automate it with a loop and have "getline" or a similar function grab the data for me.
So my main questions are:
1) Why is my push_back and print out failing?
2) How would I read in char by char with a loop to prevent me from manually putting data in?
The error I receive on run-time is:
Segmentation Fault(core dump)
Thank you everyone in advance for your assistance and knowledge!
ofstream is for output file. Use ifstream for reading the file.
vector<string> grid;
vector<string> will work in your case. But it's easier to create a 2-D array of vector with vector<vector<char>>, then you access grid[x][y]
Unlike arrays, vectors don't have any elements when they are created, so there is no valid grid[x][y] or grid[x] until you add something to it.
Try this code instead:
int main()
{
ifstream fin("test.txt");
vector<vector<char>> grid;
int row = 0;
string line;
while(fin >> line)
{
grid.resize(row + 1);
for(auto c : line)
grid[row].push_back(c);
row++;
}
for(auto row_vector : grid)
{
for(auto e : row_vector)
cout << e;
cout << "\n";
}
return 0;
}
So I have a programming project that is by far the most difficult one I've had to do thus far. We are given a file with a list of people's names, birthdays, and salaries. Among other things, we have to make sure each name is formatted in the correct way (LASTNAME, FIRST MIDDLE(if they have one))...Currently the following code is meant to extract their last name, assuming it is formatted incorrectly (FIRST MIDDLE LAST). The first line of the file is
Matthew Alan Aberegg 1963 452,627
And the correct output would be
Aberegg, Matthew Alan 1963 452,627
I have to do a few other things, but this is where I'm stuck. It is especially difficult because some names only have a first and last and only some are in the incorrect format. I commented out the first while loop, as I'm trying to get this to even work just for one name.
As a side note, the commented "cout" at the beginning is only shows up if i add an endl; to the end...otherwise it is just completely ignored. It seems like this is the case throughout the whole code. I really don't understand how that could be, either, but whatever, I have bigger problems than that.
Using Eclipse on a Mac, if that changes anything.
I'm sure there are points of misuse of cctype functions or ways I could shorten the code. Any suggestions are welcome!
Thanks for any help, seriously, I've been stuck on this for way too long.
#include <iostream>
#include <string>
#include <fstream>
#include <cctype>
using namespace std;
int main() {
//cout << "test";
ifstream fileIn; // for input files
ofstream fileOut; // for output files
string currentLine; // for analyzing current line
fileIn.open("oldretirement.txt");
fileOut.open("newretirement.txt"); // TODO: move this closer to where it's needed
// TODO: make sure file opens first (while loop?)
int count = 0; // for parsing through each line
int count2 = 0; // for rearranging the name TODO: rename/is this needed?
int pos; // finds the position of first comma
string name; // for rearranging the name if not in correct format
bool rearranged = false; // for rearranging, true if the name is rearranged
//while(!fileIn.eof()){ // while the file is not at its end
getline(fileIn, currentLine);
//cout << currentLine;
for(count = 0; count < currentLine.length(); count++){
pos = currentLine.find_first_of(',');
// cout << pos<<endl;
if(isdigit(currentLine[pos-1])){
count2 = 0;
while(count2 < currentLine.length()){ // change this condition?
if(isdigit(currentLine[count2])){
cout << count2 <<endl; //test
count2 = count2 - 2; // goes back
while(!isspace(currentLine[count2])){
count--;
}
name = currentLine.substr(count2, currentLine.find(" "));
}
count++;
}
}
}
// }
fileIn.close();
fileOut.close();
return 0;
}
Can someone please explain why only the first letters are being deleted when reading in from a data file but only on the 1/2/3 parts of the array and not the 0 part? (sorry really don't know how to explain it)(I'll only include part of what I am getting as well as data file)
What i get
GoogleyleSmith01#gmail.comyleman27ecurity question:White rabbit with a watch
Deviantartragonmaster27andalfthegreyNULL
What it's supposed to be
GoogleKyleSmith01#gmail.comKyleman27securityquestion:Whiterabbitwithawatch
DeviantartDragonmaster27GandalfthegreyNULL
And the original data file
Google;KyleSmith01#gmail.com;Kyleman27;security question:White rabbit with a watch;
Deviantart;Dragonmaster27;Gandalfthegrey; NULL;
I won't include all of the code as it shouldn't be relevant to this issue
#include<iostream>
#include <fstream>
#include <string>
#include <vector>
#include<sstream>
using namespace std;
const int NCOLS = 4;
const int NROWS = 10;
void description_and_options(string data[][NCOLS], int count[NCOLS]);
void available_options();
void view_line_data(int choice,string data[][NCOLS]);
int main()
{
ifstream file_name;//create the new file
string user_input_file;//the files name inputed by the user
int stringlength;
string read_in_array[NROWS][NCOLS];
string line;
int counter[10] = { 1,2,3,4,5,6,7,8,9,10 };
string user_option_choice;
string small_exit = "x";
string large_exit = "X";
int view_choice;
cout << "Enter the name of the input file: ";
cin >> user_input_file;
if (user_input_file.length() > 4)// check to see if its more than 4 in length
{
stringlength = user_input_file.length(); //saves length
if (user_input_file.substr(stringlength - 4, 4) == ".txt")//checks to see if its .dat
{
file_name.open(user_input_file.c_str());
if (file_name.fail())
{
cerr << "The file " << user_input_file << " failed to open.\n";//tells user if it fails
exit(1);
}
}
}
else
{
user_input_file += ".txt";//adds .dat to non .dat
file_name.open(user_input_file.c_str());
}
if (file_name.fail())
{
cout << "File failed to open" << endl;
system("PAUSE");
exit(1);
}
for (int row = 0; row <= 9; row++)
{
for (int col = 0; col < 4; col++)
{
if (getline(file_name, line, ';'))
{
file_name.ignore(1, '\n');
read_in_array[row][col] = line;
cout << read_in_array[row][col];
}
}
cout << endl;
}
//[updown][leftright]
file_name.close();
is there anyway to fix this without completely changing the code?
It is ignoring the first character because you tell it to
file_name.ignore(1, '\n');
Is going to ignore the first character in the stream after each call to getline. It looks like you are doing this because you think the ; in the file it still there. What you need to remember about getline is that it discards the delimiter you use. That means it will read until it finds a ; and then it tosses that ; out. This means you do not need to ignore it since it is no longer there.
Just removing the call to ignore is not enough to fix the issue though. Since you are trying to parse an entire line what we need to do is read the line into a stringstream and then call getline on the stream to get the individual parts. This is because just reading to ; is going to capture the newline.
A quick refactor of your code gives you something that should look like
for (int row = 0; row <= 9; row++)
{
std::string temp;
std::getline(file_name, temp)
std::stringstream ss(temp)
for (int col = 0; col < 4; col++)
{
if (getline(ss, line, ';'))
{
read_in_array[row][col] = line;
cout << read_in_array[row][col];
}
}
cout << endl;
}
You are using wrongly ifstream::ignore().
Extracts characters from the input sequence and discards them, until
either n characters have been extracted, or one compares equal to
delim.
file_name.ignore(1, '\n'); always dismiss the first letter. In your case, the first letter after ";" in line.
file_name.ignore(1, '\n'); will make the stream ignore one character from the input.
From reading your code:
For what you call "the 0 part", ignore is not called yet before the first getline in the loop.
For "parts 1/2/3", the ignore statement makes the stream skip the next character
For the remaining parts, there is either a space or a '\n' that was skipped so that the readable letter was not skipped.
I am writing a program that extracts data from a text file and encrypts it. I am having some trouble with this. First of all there is an error at the getline(data,s[i]) part. Also the text file has two sentences but it only encrypts the second sentence. The other issue with that is It encrypts one letter at a time and outputs the sentence every time. It should output just the sentence encrypted.
#include <iostream>
#include <fstream>
#include <istream>
using namespace std;
int main(){
//Declare Variables
string s;
ifstream data;
//Uses Fstream to open text file
data.open ("/Users/MacBookPro/Desktop/data.txt");
// Use while loop to extract the data from the text file
while(!data.eof()){
getline(data,s);
cout<< s << endl;
}
//Puts the data from the text file into a string array
for(int i = 0; data.good(); i++){
getline(data, s[i]);
cout<< s <<endl;
}
// encrypts the string
if(data.is_open()){
for(int i = 0; i < s.length();i++){
s[i] += 2;
cout << s << endl;
}
}
return 0;
}
In the code below you already reach the end of the stream, and store the last line on the string s.
while(!data.eof()){
getline(data,s);
cout<< s << endl;
}
My suggestion is that you use a list of strings.
vector< string > s;
string tmp;
while(!data.eof()){
getline(data,tmp);
s.push_back(tmp);
cout<< s << endl;
}
The next step you loop through the list and do the encryption
for(i=0; i < s.size(); i++)
{
// encrypt s[i]
}
Hope this helped!
First I had to add this line to get "getline" to be recognised:
#include <string>
Then, there was indeed an error with the line:
getline(data, s[i]);
This is a compilation error, that function is expecting a stream and a string, but you pass it a stream and a char.
Changing that line for:
getline(data, s);
makes your program compile.
However it probably does not do what you want at this point, since the variable i from the for is being ignored.
I suggest that you check out some documentation on the getline function, then rethink what you want to do and try again.
You can fine some doc here:
https://msdn.microsoft.com/en-us/library/vstudio/2whx1zkx(v=vs.100).aspx
Your other concern was that it output your string many times. This is normal, since your cout statement is inside in your encryption loop.
Move it outside the loop instead, to output it only one time once the encryption loop is done.
It is important to spend the time to understand what each line of your program is doing, and why you need it to achieve your goal.
Also when doing something that we find complicated, its easier to do one small part of it at a time, make sure it works, then continue with the next part.
Good Luck :)
Create a temporary string for containing each line and an integer since we're going to find the total number of lines to create an array for all of them.
string temp = "";
int numberOfLines = 0;
Now we try to find the total number of lines
while(data.good()) {
getline(data, temp);
cout << temp << endl;
numberOfLines++;
}
Now we can create an array for all of the lines. This is a dynamic array which you can read on it for more information.
string * lines = new string[numberOfLines];
Now is the time to roll back and read encrypt all the lines. But first we have to go back to first position of file. That's why we use seekg
data.seekg(data.beg);
For each line we read, we'll put in the array and loop through each character, encrypt it and then show the whole sentence.
int i = 0;
while (data.good()) {
getline(data, lines[i]);
i++;
for (int j = 0; lines[i].size(); j++ ) {
lines[i].at(j) += 2;
}
cout << lines[i] << endl;
}
Voila!
s[i] is a character in the string (actually, a character reference), not the string object itself. string::operator[] returns a char& in the docs. See here.
Consider declaring a std::vector<string> string_array; and then use the string_array.push_back(data) member function to append strings from the file onto the vector. Use a for loop to iterate through the vector at a later time with a vector<string>::iterator or the std::vector::size function to get the length of the vector for a traditional for loop (via a call to string_array.size()). Use the square brackets to get each string from the vector (string_array[0 or 1 or etc.]).
Get characters from each string in the vector by using something like string_array[n][m] for the mth character of the nth string. Iterating over each character should be as simple as using the string::length member function to get the string length, and then another for loop.
Also, std::cout << s << std::endl is being used in the wrong places. To output each character, try std::cout << s[i] << std::endl instead, or printf("%c", s[i]), whichever you like.
I'd suggest not using an array to hold strings from the file because you don't know what the array's length will be at runtime (the file size could be unbounded), so a vector is better suited for this case.
Finally, if you need code, there's a beginner's forum post here that I think will help you out. It has a lot of code like yours, but you'll have to modify it for your purposes.
Finally, please use:
std::someCPPLibraryFunction(args);
instead of...
using namespace std;
someCPPLibraryFunction(args);
I have attached my full source code of my program that can open a .txt file. It doesn't execute after the cout << length. I am trying to store the .txt file information in memory by using an array.
#include <iostream>
#include <string.h>
#include <fstream>
using namespace std;
char filename[128];
char file[10][250];
int count;
int length;
string line;
int main ()
{
int count = 0;
int length = 0;
cout << "Filename: ";
cin.clear();
cin.getline(filename, sizeof(filename));
string new_inputfile(filename);
ifstream inputfiles (new_inputfile.c_str());
if(!inputfiles.is_open())
{
cout << "File could not be opened. \n ";
}
else
{
for (int i=0; getline(inputfiles,line); i++)
{
length++;
}
cout << length;
// char file[length][250]; <- How can I create the array based on the length variable?
// CODE DOES NOT EXECUTE AFTER THIS.
while(!inputfiles.eof() && (count<10))
{
inputfiles.getline(file[count],250);
count++;
}
for(int i=0; i < count; i++)
{
cout << file[i] << endl;
}
}
inputfiles.close();
return 0;
}
Also, since file[] is char, say for example file[1] contained the char Name=Mike, how do I strip off everything before the =. I want just Mike. I know with string, I can use substr() method, but I don't know for char.
This is horribly wasteful way to count number of lines in a file.
for (int i=0; getline(inputfiles,line); i++) // i is also completely useless here
{
length++;
}
You're reading the whole file only to throw everything away and start again! And after this loop is done, inputfiles.eof() will be true and you'll never enter neither the next while loop nor the last for loop (because i == count). Execution skips directly to inputfiles.close() and then you return from main.
I suggest you work on the line string as you go:
for ( ; getline(inputfiles, line); )
{
// do stuff with line and ditch the global char arrays
}
If you want store the lines for later, well, just save them :) The easiest thing to do is to use a vector:
std::vector<std::string> all_them_lines;
while (getline(file, line) all_them_lines.emplace_back(line);
There, the entire file is now saved in all_them_lines, line by line. You can access them just like you would in an array, like all_them_lines[0]. You also don't need to know the number of lines beforehand - vectors expand automatically when you add stuff to them.
Now to parse a line and extract formatted input from it, check out what stringstream class has to offer.
You asked:
// char file[length][250]; <- How can I create the array based on the length variable?
Declare file as:
char (*file)[250] = NULL;
and then,
file = new char[length][250];
Make sure you call delete [] file before the end of the function.
You said:
// CODE DOES NOT EXECUTE AFTER THIS.
You can rewind the stream and start reading from it again.
inputfiles.seekg(0);
count = 0;
while(!inputfiles.eof())
{
inputfiles.getline(file[count],250);
count++;
}