Reading from text file -- Separating elements of line [duplicate] - c++

This question already has answers here:
Read file line by line using ifstream in C++
(8 answers)
Closed 6 years ago.
I have the following text file from which I am attempting to read each line and then store the integer component and the string component separately. Here is the text file:
RUID Name
4325 name1
RUID Name
5432 name2
6530 name3
RUID Name
1034 name4
2309 name5
Here is the code I attempting to read with:
int main()
{
// Initialize Lists
LinkedList list1, list2, list3;
// Initialize Counter
int counter = 0;
// Entry containers
const int size = 12;
char entry[size];
string name[size];
string RUID[size];
// BEGIN: "read.txt"
// Open
ifstream studDir;
studDir.open("read.txt");
// Read
while (studDir.is_open())
{
if (studDir.eof())
{
cout << "Reading finished" << endl;
break;
}
else if (!studDir)
{
cout << "Reading failed" << endl;
break;
}
studDir.getline(entry, size);
if (entry != "RUID Name")
{
cout << entry << " " << endl;
}
}
return 0;
}
Could anyone recommend a method that would allow me to ignore the "RUID Name" line as well as separate the integer and string portion of the relevant lines. I've tried several strategies with little success. Also I wish to then write the output of the sorted list to a text file.

You should rewrite your loop like this:
// Entry containers
const size_t size = 12;
std::string entry;
string name[size];
string RUID[size];
size_t index = 0;
// ...
while (index < size && std::getline(studDir,entry)) {
if (entry != "RUID Name") {
cout << entry << " " << endl;
std::istringstream iss(entry);
if(!(iss >> RUID[index] >> name[index])) {
std::cout << "Reading failed" << endl;
break;
}
++index;
}
}

Related

how to update values in a input file? For exmaple the input file has a value of 10. How can the user change the value by cin a new value? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
This is what I tried. I tried strcpy, substituting the value and using in_file again 3 of them can't work. This program is supposed to use the data in a input file and then does functions 1-4 selected by user.
List all products
Search the price of a product
Update the price of a product
Exit
If the user chooses to exit, the program will write the updated data in the array of structures to the file.
The product information includes product number, description and price.
#include <iomanip>
#include <fstream>
#include <cstring>
using namespace std;
typedef struct
{
char product_num[5];
char product_des[29];
float price;
}PRODUCT_TYPE;
int main()
{
ifstream in_file("input practical 12 part d.txt");
if (!in_file)
{
cout << "Error opening input file" << endl;
}
else
{
PRODUCT_TYPE product[50];
int index = -1;
int choice;
in_file >> product[++index].product_num;
cout << "Succesful run" << endl;
cout << "Menu (Type number for the function)" << endl;
cout << "1. List all products\n2.Search the price of a product\n3.Update the price of a product\n4.Exit\n";
cin >> choice;
if (choice == 1)
{
cout << setw(18) << left << "Product Index" << setw(35) << left << "Product Description" << "Price" << endl;
}
else if (choice == 3)
{
cout << "Enter the product number to change its price: ";
cin >> product[index].product_num;
cout << "Enter the new price of the product: ";
cin >> product[index].price;
}
else if (choice == 4)
{
cout << setw(18) << left << "Product Index" << setw(35) << left << "Product Description" << "Price" << endl;
}
while (in_file)
{
in_file >> product[index].product_des >> product[index].price;
if (choice == 1)
{
cout << setw(18) << left << product[index].product_num << setw(35) << left << product[index].product_des << product[index].price << endl;
}
else if (choice == 2)
{
cout << "Type the product number to get the price" << endl;
cout << "Product Number: ";
cin >> product[index].product_num;
cout << "Price: ";
cout << product[index].price << endl;
}
else if (choice == 4)
{
cout << setw(18) << left << product[index].product_num << setw(35) << left << product[index].product_des << product[index].price << endl;
}
in_file >> product[++index].product_num;
}
in_file.close();
out_file.close();
}
return 0;
}```
Unfortunately changes in existing files are not that easy. The problem is that data are stored byte after byte in a file on a disk (sometimes even not that). So, if you have data in your file (always one byte) like the below
Position in file: 0 1 2 3 4 5 9 7 8 9 . . .
Bytes as decimal: . . . 49 50 51 61 62 63 . . .
Bytes as character: . . . 1 2 3 A B C . . .
And you want to update 1 2 3 at position 3 by 1 2 3 4 5 then you would need to shift all folowing bytes in the file by 2 characters, because 12345 is 2 bytes longer than 123. Nobody can do that. And it will not be done automatically.
So, there are basically 2 approaches to solve such problems.
Open the source file -> Read all data into memory -> Close source file -> Do the modifications in memory -> Open source file in overwrite mode -> Write new data.
Use a temporary file. -> Open the source file for input -> Open a temporary file for output -> Read source file line by line -> Do modifications for each read line immediately -> Write result to temp file -> After all source file data have been read, close both files -> Delete the original source file. Rename the tempfile to the old source file name.
(3. If the length of the data, that you want to modifiy, is the same than the old data, then you could move the file write pointer and overwrite the old data. But you need always the same length. So, most often not feasible)
There are of course more solutions, but those 2 approaches are often used.
Please see some pure example codes. Just plain example, unrelated to your code. No productive code. No error checking. Just to give you an idea, how it could work.
Method 1
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
std::vector<std::string> readFile(const std::string& filename) {
// Here we will store all the data from the file
std::vector<std::string> fileData;
// Open the source file
std::ifstream fileStream(filename);
// Read line by line and add it to our fileData
std::string line;
while (std::getline(fileStream, line)) {
fileData.push_back(line);
}
return fileData;
}
void writeFile(std::vector<std::string>& fileData, const std::string& filename) {
// Open file for output
std::ofstream fileStream(filename);
// Write all data to file
for (const std::string& line : fileData)
fileStream << line << '\n';
}
int main() {
// Aproach with read complete file to local variable, modify and the store again
const std::string dataFileName("r:\\test.txt");
// Get file content
std::vector<std::string> data = readFile(dataFileName);
// Now go through all records and do something
for (std::string& line : data) {
// If some condition is met then do something, for example modify
if (line == "Line1") line += " modified";
}
// And then write the new data to the file
writeFile(data, dataFileName);
return 0;
}
Method 2:
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
int main() {
// Aproach with temp file, remove and rename, and on the fly change
const std::string dataFileName("r:\\test.txt");
const std::string tempFileName("r:\\temp.txt");
{
// Open the source file with data
std::ifstream dataFileStream(dataFileName);
// Open the temporary file for output
std::ofstream tempFileStream(tempFileName);
// Now read the source file line by line with a simple for loop
std::string line;
while (std::getline(dataFileStream, line)) {
// Identify the line that should be deleted and do NOT write it to the temp file
if (line != "SearchString") { // Or any other condition
// Write only, if the condition is not met
tempFileStream << line << '\n';
}
}
} // The end of the scope for the streams, will call their destructor and close the files
// Now, remove and rename
std::remove(dataFileName.c_str());
std::rename(tempFileName.c_str(), dataFileName.c_str());
return 0;
}

how to store user input into array in C++

I am new to C++, and want to ask this basic question
what i want: user input data like 2:3American, 4:2China (this means my country team wins 2 points lose 3 points againts American. my country team win 4 points and China team win 2 points)
in console:
please input the result for your team against other teams, input negative number to exit
2:3American
4:2China
-1
result win:1
lose:1
draw:0
If there is no specific encoding is given to you by some authority, use as simple as possible. A better was "2 3 American 4 2 China". So that you only deal with a simple for loop.
The result line is not calculated. Convert each string to integer to calculate.
int main( int argc, char* argv[]) {
std::vector<std::string> arguments(argv + 1, argv + argc);
std::cout << "arguments contains \n";
for (std::vector<std::string>::iterator it = arguments.begin() ; it != arguments.end(); ++it) {
int firstPos = it->find_first_of(":");
int secPos = 0;
std::string firstInteger = it->substr(0,firstPos);
std::string secondInteger;
if ( firstInteger.compare("-1") == 0 ) {
std::cout << "breaking \n";
return 0;
} else {
std::cout << " f=<" << firstInteger << ">";
secPos = it->find_first_not_of( "012345678:", firstPos);
if ( secPos == std::string::npos )
std::cout << "not found";
std::cout << " s=<" << it->substr(firstPos+1 ,secPos-firstPos-1 ) << "> ";
std::string teamName = it->substr(secPos);
std::cout << teamName ;
std::cout << std::endl;
}
}
std::cout << '\n';
return 0;
}
I've written a code similar to this problem a long time ago. made a small change to solve your problem.
So i think this is what you want
INPUT "2:3American 4:2China -1" (SINGLE LINE)
OUTPUT as expected
#include <iostream>
using namespace std;
size_t non_int(int index,string* s)
{
int i=index;
for( i=index;i<s->length();i++){
if(s->at(i) >= '0' && s->at(i) <= '9')
{
// cout << s->at(i) << " is a Digit";
}
else{
return (i-1)<index?(std::string::npos):(i-1);
}
}
return (i-1)<index?(std::string::npos):(i-1);;
}
int main()
{
cout << "Please input the match result such as 2:3American " << endl;
string str;
std::getline (std::cin,str);
//cout<<str;// i want to see did the first user input stored in array. But seems the console..does not print out temp[0] and just skipt it
int win,lose,draw=0;
std::size_t found = 0;
string s1,s2;
int i1,i2;
std::size_t f1,f2;
while( found !=std::string::npos)
{
f1 = str.find(":",found);
if (f1!=std::string::npos){
i1 = stoi(str.substr(found,f1-found));
f2 = non_int(f1+1,&str);
if (f2!=std::string::npos){
i2 = stoi(str.substr(f1+1,f2-f1));
if(i1>i2) win++;
else if(i1<i2)lose++;
else draw++;
}
else {
cout<<"ERROR :invalid input ";
}
}
else {
//exit on -ve input
// cout<<"ERROR 45 ";
}
found = str.find(" ",found+1);
}
cout<<"win:"<<win<<"lose:"<<lose<<"draw:"<<draw<<endl;
return 0;
}
Step 1:
Define a class that represents an input token.
struct Segment
{
int myCountryScore;
int otherCountryScore;
std::string otherCountry;
};
Step 2
Define an input function that reads a Segment from a stream.
std::istream& operator>>(std::istream& s, Segment& data)
{
Segment tmp;
char sep;
int firstNumber;
bool good = false;
if (s >> firstNumber && firstNumber >= 0)
{
tmp.myCountryScore = firstNumber;
if (s >> std::noskipws >> sep >> tmp.otherCountryScore >> tmp.otherCountry >> std::skipws) && (sep == ':'))
{
// The read worked. Copy it to the output object.
data = tmp;
good = true;
}
}
if (!good) {
// If there was an error reading.
// Or we reached the end (negative number read)
// Then set the state of the stream to failure mode.
s.setstate(std::ios::failbit);
}
return s;
}
Step 3
Write a loop that reads Segments from a stream in a loop.
Segment object;
while(std::cin >> object) {
// You have correctly read an object
// Add your code to handle it here.
}
Step 3 Alternative.
Rather than read the Segment one by one you can copy them into a vector simply using a stream iterator.
std::vector<Segment> data(std::istream_iterator<Segment>(std::cin),
std::istream_iterator<Segment>());

data written to text file is partially corrupt and irretrievable

template <class T>
void savetext(T *a, char const *b) //writes to text file inside .sln however the text file is corrupt
{
ofstream fout(b, ios::out);
for (int i = 0; a[i] != '\0'; i++)
fout << a[i] << endl;
cout << "Text file successfully written to." << endl;
}
template <class T>
void gettext(T *a, char const *b) //this is where the error occurs: inside the text file it puts the right values along with piles of junk. Why is this?
{
ifstream fin(b, ios::in);
if (fin.is_open())
{
cout << "File opened. Now displaying .txt data..." << endl;
for (int i = 0; a[i]!= '\0'; i++)
{
fin >> a[i];
}
cout << "Data successfully read." << endl;
}
else
{
cout << "File could not be opened." << endl;
}
}
int main()
{
const int n1 = 5, n2 = 7, n3 = 6;
int a[n1], x, y, z;
float b[n2] = {};
char c[n3] = "";
//Begin writing files to text files which I name herein.
cout << "Writing data to text 3 text files." << endl;
savetext(a, "integer.txt");
savetext(b, "float.txt");
savetext(c, "string.txt");
cout << endl;
//Retrieve the text in the files and display them on console to prove that they work.
cout << "Now reading files, bitch!" << endl;
gettext(a, "integer.txt");
gettext(b, "float.txt");
gettext(c, "string.txt");
cout << endl;
return 0;
system("PAUSE");
}
Hello, and good evening. I have a C++ program that is currently writing data (integer, floats and chars) to 3 separate text files. However, when it writes the data to the text files two things happen: the data is inside the text file, but so is a bunch of unintelligible wingdings text and large numbers and large negative numbers -- data which I never entered in.
As a result of this, I'm not able to retrieve the information from the text file and I'm not able to display the information of the text file. How do I solve that problem? Thank you.
Your first problem is that you are writing uninitialized data to your files.

Dynamic Variable based off txt file

I have to create a school library as an OOP assignment. I'm finding it very difficult to understand, my question here is:
int RANGE = total_books;
the total_books should represent the current books in the text file.
The formatting makes it read 3 parts of info (title, author, genre). How can I point to this between functions?
I want to load the program and read the file in order to see how many there are currently (lets say 7 books, so the variable should be 7 * 3 = 21). Then when the user views the file, it will display the 7 books.
Currently it's static: I have it set to 21. if I add another book it will only read the first 7 books. If I set it to 24 and there are 7 books (not 8 as needed) it will crash. I've tried looking on these forums and other places online, got the "C++ Programming in easy steps" book, it's where I got this formatting code but it's not very useful.
#include "stdio.h"
#include "malloc.h"
#include "stdlib.h"
#include "string.h"
#include "conio.h"
#include "fstream"
#include "iostream"
using namespace std;
unsigned int number_of_books = 0; //this is based on the number of books *3
int total_books = number_of_books * 3; //*3 to read 1 more book
class Book
{
private: // properties
char Title[16];
char Author[16];
char Genre[16];
public: // methods
int iDetailsGet(void);
int iDetailsShow(void);
int iRecordWrite(void);
};
int Book::iDetailsGet(void)
{
// prompt for the data
fflush(stdout);
puts("\n \t !USE_UNDERSCORE_FOR_SPACE!");
puts("\n \t Please enter the Book name: ");
fflush(stdin);
scanf("%s", Title, 16);
fflush(stdout);
puts("\n \t Please enter the Author: ");
fflush(stdin);
scanf("%s", Author, 16);
fflush(stdout);
puts("\n \t Please enter the Genre: ");
fflush(stdin);
scanf("%s", Genre, 16);
// Get total number of lines(books)
FILE *infile = fopen("books.txt", "r");
int ch;
while (EOF != (ch = getc(infile)))
if ('\n' == ch)
++number_of_books; // read from variable above but static.
printf("%u\n", number_of_books);
//return to menu
int main();
} // end method definition
int Book::iDetailsShow()
{
system("CLS");
int RANGE = total_books; // should be dynamically read on start up
string tab[RANGE];
int i = 0, j = 0;
ifstream reader("books.txt");
if(!reader)
{
cout << "Error Opening input file" << endl;
return -1;
}
while(!reader.eof())
{
if((i + 1) % 3 == 0) // the 3 read title,author,genre then adds new line
getline(reader, tab[i++], '\n');
else
getline(reader, tab[i++], '\t');
}
reader.close();
i = 0;
while (i < RANGE)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Title: " << tab[i++] << endl;
cout << "Author: " << tab[i++] << endl;
cout << "Genre: " << tab[i++] << endl;
}
int main();
} // end method definition
// code for the method: iRecordWrite(void)
int Book::iRecordWrite(void)
{
ofstream NewBook("books.txt", ios::app);
if (!NewBook)
{
printf("Error Recording Book");
return -1;
}
NewBook << " " << Title << " " << Author << " " << Genre << endl;
NewBook.close();
int main();
} // end of method deinition
Thanks!
Instead of initialising total_books based on number_of_books when it's declared, you should probably set it after actually reading number_of_books. Variables, whether global or in a scope, don't update themselves dynamically. So, you could have something like this:
int number_of_books = 0;
void read_number_of_books() {
// parse input file for value
total_books = number_of_books * 3;
}
Is that what you're looking for?

Printing Data In The Right Pattern

I've written a function that goes through the text file and reads all the data's and prints them out, but the format of displaying data's are wrong and it just prints out data line by line, like this:
james
c 18 6 endah regal
male
0104252455
rodgo.james
kilkil
and what i'm looking for displaying data is something like this (which is not currently happening ):
Name : james
Address : c 18 6
Gender : Male
Contact : 0104252455
Username : rodgo.james
Password : kilkil
and here is the function :
int molgha() {
ifstream in("owner.txt");
if (!in) {
cout << "Cannot open input file.\n";
return 1;
}
char str[255];
while (in) {
in.getline(str, 255); // delim defaults to '\n'
if (in) cout << str << endl;
}
system("pause");
in.close();
}
keep in mind that this text file contains the records of owners registered to the system, hence we may need to print out maybe 3 sets of owner data's with the same pattern without any errors, hence what is the best way to display data's like that continuously ?
You're not printing out the desired labels of Name,Address etc in your code. You have two options -
1) Write out the labels before the data in the actual file itself, and leave the printing code as-is
2) Have a struct or a class with the members name, address etc and a function to print the contents
struct FileEntry{
string name;
string address;
.
.
.
void printContents(){
cout << "Name: " << name << endl;
cout << "Address: " << address << endl;
// etc etc
}
}
If you want to have varying numbers of records per file just put a number at the top of the file, i.e if the file holds 100 records put 100 as the first piece of data to read in and use it in your processing
int numRecords;
ifstream in;
if(in.open("owners,txt")){
numRecords << in;
for(int record = 0; record < numRecords; records++){
//read the info and output it here
}
You want to store your output names names, something like this:
std::vector<std::string> names { "Name", "Address", "Gender", "Contact", "Username", "Password" };
take an interator to it:
auto it = names.begin();
and print in your while loop:
if (in) cout << *it++ << " : " << str << endl;
Just add a array of labels, and print them based on the line you are getting from the file.
const string labels[6] = {
"Name", "Address", "Gender", "Contact", "Username", "Password"
};
int i = 0;
while (in) {
in.getline(str, 255); // delim defaults to '\n'
if (in) {
if (i == 6) i = 0;
cout << labels[i++] << " : " << str << endl;
}
}
So to re-state your question :
How can I add the fields Name: , Address: , etc to the output.
I would suggest the following approach :
Declare the field names in a static array :
const char* fieldNamesArray[6] = { "Name","Address","Gendre", "Contact","Username","Password"};
Inside your read / write function , consume each non-empty lines and assume that all entries have 6 fields and all time in the same order :
int curField=0;
while(in)
{
in.getLine(str,255);
if (strlen(str)>0)
{
cout<< fieldsNamesArray[curField] << " : " << str;
curField++;
}
if (curField>=6)
{
curField=0;
}
}