C++ how to read what I want from a file? - c++

How can I assign a variable from my C++ code a value from a structured .txt file for example?
If I have this following input.txt structured like this:
<Name> "John Washington"
<Age> "24"
<ID> "19702408447417"
<Alive Status> "Deceased"
In my c++ code if I have
ifstream read("input.txt",ios::in);
char name[64];
read>>name;//name will point to John Washington string
int age;
read>>age;// cout<<age will return 24;
int ID;
read>>ID;// cout<<ID will return 19702408447417
char Alivestatus[32];
read>>Alivestatus;//Alivestatus will point to Deceased string;
How can I make it work like above?

As #πάντα ῥεῖ mentioned in the comments, you will need to implement a parser that can interpret the <> tags within your file. Additionally, I would recommend reconsidering the data types.
Specifically, given that there's no special reason that you're using char [], please switch to std::string. I don't know the use case of your code, but if the input.txt happens to contain data thats larger than the size of the arrays, or even worse if the input is user-controlled, this can easily lead to Buffer Overflows and unwanted exploits. std::string also has the benefit of being standardized, optimized, much more friendly than char arrays, and has a variety of useful algorithms and functions readily available for use.
With regards to text file parsing, you can perhaps implement the following:
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream input_file("input.txt");
std::string name_delimeter("<Name> ");
std::string age_delimeter("<Age> ");
std::string id_delimeter("<ID> ");
std::string alive_delimeter("<Alive Status> ");
std::string line;
std::getline(input_file,line);
std::string name(line,line.find(name_delimeter) + name_delimeter.size()); // string for the reasons described above
std::getline(input_file,line);
int age = std::atoi(line.substr(line.find(age_delimeter) + age_delimeter.size()).c_str());
std::getline(input_file,line);
std::string id(line,line.find(id_delimeter) + id_delimeter.size()); // the example ID overflows 32-bit integer
// maybe representing is a string is more appropriate
std::getline(input_file,line);
std::string alive_status(line,line.find(alive_delimeter) + alive_delimeter.size()); // string for the same reason as name
std::cout << "Name = " << name << std::endl << "Age = " << age << std::endl << "ID = " << id << std::endl << "Alive? " << alive_status << std::endl;
}
The basis of the code is just to read the file as it is structured and construct the appropriate data types from them. In fact, because I used std::string for most of the data types, it was easy to build the correct output by means of std::string's constructors and available functions.
Maybe you are performing this in a loop, or the file has several structures. To approach this problem, you can make a Record class that overloads operator >> and reads in the data as required.

Related

Users Manually Enter Input and Output Paths C++

I have no idea about C++, but I've been assigned to edit this piece of code:
// Setup path information for output file from environmental variables
char * path = new char[100];
path = getenv("MODEL_MERGE");
char * templatePath = new char[100];
char * outputPath = new char[100];
strcpy(templatePath, path);
strcat(templatePath, "infile location");
strcpy(outputPath, path);
strcat(outputPath,"outfile location");
cout << "temp: " << templatePath << endl;
cout << "out: " << outputPath << endl;
//input output file streams for reading/writing to files
ifstream readFile(templatePath);
ofstream outFile(outputPath);
My goal is to replace the "infile location" and "outfile location", which currently point to specific files. I want the user to be able to enter the file names when running from command prompt. Sorry if this is something as simple as <<cin, but I couldn't get that to work, and I have zero experience with this language.
Got it! Everything above was replaced by:
//User inputs paths
string input;
string output;
cout<<"Input path?"<<endl;
cin>> input;
cout<<"output path?"<<endl;
cin>> output;
//input output file streams for reading/writing to files
ifstream readFile(input.c_str());
ofstream outFile(output.c_str());`
Thanks everyone for the help!
There is enough wrong with the code supplied to OP to be worth a quick going over in addition to pointing the OP in a useful direction.
First, no test for NULL on the call to getenv. If MODEL_MERGE doesn't exist, NULL is returned and then used in string copies. BOOM!
Second, newing all those arrays. Dynamically allocate only as a last resort. new must be pared with at least one delete, depending on the code's flow, to return the allocated memory for reuse when no longer needed. Since there seems to no need to dynamically allocate and the sizes of the arrays are known, they should have been defined as char templatePath[100];. Less memory management to be dealt with and effectively no possibility of leakage.
Third renders point two obsolete. Rather than using char arrays, use strings where possible. Not only do they handle all of the memory management, including resizing as needed rather than trampling out of bounds, for you, they also perform routine tasks like copying and appending with much less fuss. This bit I'll demonstrate below.
Proper use of cin and cout is well detailed on a number of sites so I won't go over it here.
Also note I've removed the need for using namespace std; by explicitly stating the namespace at use. Read why using namespace std; is often a bad idea.
#include <fstream>
#include <iostream>
int main()
{
char * Model_MergePath = getenv("MODEL_MERGE");
if (Model_MergePath != NULL)
{ //MODEL_MERGE is defined
std::string path(Model_MergePath); //replace icky and fault-prone char array
std::string templatePath = path; // copy strings with =
std::string outputPath; // not assigning path here so I can demonstrate
//something else later
std::string inFileLoc; // new throw away variables for user input.
std::string outFileLoc; // could use the same var for both. I didn't for clarity
std::cin >> inFileLoc; // get input
templatePath += inFileLoc; // append to strings with +=
std::cin >> outFileLoc;
outputPath = path + outFileLoc; // concatenate strings with +
// validate paths for correctness and possible intrusion attempts here
// this, I'm afraid, is up to the OP as the security requirements are unknown
std::cout << "temp: " << templatePath << std::endl;
std::cout << "out: " << outputPath << std::endl;
//input output file streams for reading/writing to files
std::ifstream readFile(templatePath);
// older C++ compilers may require a c-style string as the file path
std::ofstream outFile(outputPath.c_str());
// do stuff with readFile and outFile
// remove the deletes that should have corresponded to the replaced `new`s
return 0;
}
else
{ //MODEL_MERGE is NOT defined
std::cerr << "Cannot find environment variable MODEL_MERGE. Exiting." << std::endl;
return -1;
}
}

C++ file input with mixed delimiters and data types

I am trying to input data from a text file:
The line format is as follows...
String|String|int double
Example:
Bob|oranges|10 .89
I can get the line in as a string using
Getline(infile, line)
I don't understand how to break the line into the distinct variables from the string variable.
Thanks
for a start you could write some good old fashioned c code using strchr.
Or use string.find / find_first_of if you are using std::String
http://www.cplusplus.com/reference/string/string/find_first_of/
You marked this as C++. So perhaps you should try to use formatted extractors ...
Here is a 'ram' file (works just like a disk file)
std::stringstream ss("Bob|oranges|10 .89");
// this ^^^^^^^^^^^^^^^^^^ puts one line in file
I would use getline for the two strings, with bar terminator
do {
std::string cust;
(void)std::getline(ss, cust, '|'); // read to 1st bar
std::string fruit;
(void)std::getline(ss, fruit, '|'); // read to 2nd bar
Then read the int and float directly:
int count = 0;
float cost;
ss >> count >> cost; // the space char is ignored by formatted extraction
std::cout << "\ncust: " << cust << "\n"
<< " " << count << " " << fruit
<< " at $" << cost
<< " Totals: " << (float(count) * cost) << std::endl;
if(ss.eof()) break;
}while(0);
If you are to handle more lines, you need to find the eoln, and repeat for every record of the above style.
This approach is extremely fragile (any change in format will force a change in your code).
This is just to get your started. It has been my experience that using std::string find and rfind is much less fragile.
Good luck.

What exactly does stringstream do?

I am trying to learn C++ since yesterday and I am using this document: http://www.cplusplus.com/files/tutorial.pdf (page 32). I found a code in the document and I ran it. I tried inputting Rs 5.5 for price and an integer for quantity and the output was 0.
I tried inputting 5.5 and 6 and the output was correct.
// stringstreams
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main ()
{
string mystr;
float price = 0;
int quantity = 0;
cout << "Enter price: ";
getline (cin,mystr);
stringstream(mystr) >> price;
cout << "Enter quantity: ";
getline (cin,mystr);
stringstream(mystr) >> quantity;
cout << "Total price: " << price*quantity << endl;
return 0;
}
What exactly does the mystring command do? Quoting from the document:
"In this example, we acquire numeric values from the standard input
indirectly. Instead of extracting numeric values directly from the
standard input, we get lines from the standard input (cin) into a
string object (mystr), and then we extract the integer values from
this string into a variable of type int (quantity)."
My impression was that the function will take an integral part of a string and use that as input.
Sometimes it is very convenient to use stringstream to convert between strings and other numerical types. The usage of stringstream is similar to the usage of iostream, so it is not a burden to learn.
Stringstreams can be used to both read strings and write data into strings. It mainly functions with a string buffer, but without a real I/O channel.
The basic member functions of stringstream class are
str(), which returns the contents of its buffer in string type.
str(string), which set the contents of the buffer to the string argument.
Here is an example of how to use string streams.
ostringstream os;
os << "dec: " << 15 << " hex: " << std::hex << 15 << endl;
cout << os.str() << endl;
The result is dec: 15 hex: f.
istringstream is of more or less the same usage.
To summarize, stringstream is a convenient way to manipulate strings like an independent I/O device.
FYI, the inheritance relationships between the classes are:
From C++ Primer:
The istringstream type reads a string, ostringstream writes a string, and stringstream reads and writes the string.
I come across some cases where it is both convenient and concise to use stringstream.
case 1
It is from one of the solutions for this leetcode problem. It demonstrates a very suitable case where the use of stringstream is efficient and concise.
Suppose a and b are complex numbers expressed in string format, we want to get the result of multiplication of a and b also in string format. The code is as follows:
string a = "1+2i", b = "1+3i";
istringstream sa(a), sb(b);
ostringstream out;
int ra, ia, rb, ib;
char buff;
// only read integer values to get the real and imaginary part of
// of the original complex number
sa >> ra >> buff >> ia >> buff;
sb >> rb >> buff >> ib >> buff;
out << ra*rb-ia*ib << '+' << ra*ib+ia*rb << 'i';
// final result in string format
string result = out.str()
case 2
It is also from a leetcode problem that requires you to simplify the given path string, one of the solutions using stringstream is the most elegant that I have seen:
string simplifyPath(string path) {
string res, tmp;
vector<string> stk;
stringstream ss(path);
while(getline(ss,tmp,'/')) {
if (tmp == "" or tmp == ".") continue;
if (tmp == ".." and !stk.empty()) stk.pop_back();
else if (tmp != "..") stk.push_back(tmp);
}
for(auto str : stk) res += "/"+str;
return res.empty() ? "/" : res;
}
Without the use of stringstream, it would be difficult to write such concise code.
To answer the question. stringstream basically allows you to treat a string object like a stream, and use all stream functions and operators on it.
I saw it used mainly for the formatted output/input goodness.
One good example would be c++ implementation of converting number to stream object.
Possible example:
template <class T>
string num2str(const T& num, unsigned int prec = 12) {
string ret;
stringstream ss;
ios_base::fmtflags ff = ss.flags();
ff |= ios_base::floatfield;
ff |= ios_base::fixed;
ss.flags(ff);
ss.precision(prec);
ss << num;
ret = ss.str();
return ret;
};
Maybe it's a bit complicated but it is quite complex. You create stringstream object ss, modify its flags, put a number into it with operator<<, and extract it via str(). I guess that operator>> could be used.
Also in this example the string buffer is hidden and not used explicitly. But it would be too long of a post to write about every possible aspect and use-case.
Note: I probably stole it from someone on SO and refined, but I don't have original author noted.
You entered an alphanumeric and int, blank delimited in mystr.
You then tried to convert the first token (blank delimited) into an int.
The first token was RS which failed to convert to int, leaving a zero for myprice, and we all know what zero times anything yields.
When you only entered int values the second time, everything worked as you expected.
It was the spurious RS that caused your code to fail.

Calculating the info-hash of a torrent file

I'm using C++ to parse the info hash of a torrent file, and I am having trouble getting a "correct" hash value in comparison to this site:
http://i-tools.org/torrent
I have constructed a very simple toy example just to make sure I have the basics right.
I opened a .torrent file in sublime and stripped off everything except for the info dictionary, so I have a file that looks like this:
d6:lengthi729067520e4:name31:ubuntu-12.04.1-desktop-i386.iso12:piece lengthi524288e6:pieces27820:¡´E¶ˆØËš3í ..............(more unreadable stuff.....)..........
I read this file in and parse it with this code:
#include <string>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <iostream>
#include <openssl/sha.h>
void printHexRep(const unsigned char * test_sha) {
std::cout << "CALLED HEX REP...PREPPING TO PRINT!\n";
std::ostringstream os;
os.fill('0');
os << std::hex;
for (const unsigned char * ptr = test_sha; ptr < test_sha + 20; ptr++) {
os << std::setw(2) << (unsigned int) *ptr;
}
std::cout << os.str() << std::endl << std::endl;
}
int main() {
using namespace std;
ifstream myFile ("INFO_HASH__ubuntu-12.04.1-desktop-i386.torrent", ifstream::binary);
//Get file length
myFile.seekg(0, myFile.end);
int fileLength = myFile.tellg();
myFile.seekg(0, myFile.beg);
char buffer[fileLength];
myFile.read(buffer, fileLength);
cout << "File length == " << fileLength << endl;
cout << buffer << endl << endl;
unsigned char datSha[20];
SHA1((unsigned char *) buffer, fileLength, datSha);
printHexRep(datSha);
myFile.close();
return 0;
}
Compile it like so:
g++ -o hashes info_hasher.cpp -lssl -lcrypto
And I am met with this output:
4d0ca7e1599fbb658d886bddf3436e6543f58a8b
When I am expecting this output:
14FFE5DD23188FD5CB53A1D47F1289DB70ABF31E
Does anybody know what I might be doing wrong here? Could the problem lie with the un-readability of the end of the file? Do I need to parse this as hex first or something?
Make sure you don't have a newline at the end of the file, you may also want to make sure it ends with an 'e'.
The info-hash of a torrent file is the SHA-1 hash of the info-section (in bencoded form) from the .torrent file. Essentially you need to decode the file (it's bencoded) and remember the byte offsets where the content of the value associated with the "info" key begins and end. That's the range of bytes you need to hash.
For example, if this is the torrent file:
d4:infod6:pieces20:....................4:name4:test12:piece lengthi1024ee8:announce27:http://tracker.com/announcee
You wan to just hash this section:
d6:pieces20:....................4:name4:test12:piece lengthi1024ee
For more information on bencoding, see BEP3.
SHA1 calculation is just as simple as what you've written, more or less. The error is probably in the data you're feeding it, if you get the wrong answer from the library function.
I can't speak to the torrent file prep work you've done, but I do see a few problems. If you'll revisit the SHA1 docs, notice the SHA1 function never requires its own digest length as a parameter. Next, you'll want to be quite certain the technique you're using to read the file's contents is faithfully sucking up the exact bytes, no translation.
A less critical style suggestion: make use of the third parameter to SHA1. General rule, static storage in the library is best avoided. Always prefer to supply your own buffer. Also, where you have a hard-coded 20 in your print function, that's a marvelous place for that digest length constant you've been flirting with.

Extracting lines from .txt file, then store words into separate arrays | C++

Our professor gave us this assignment, where we have a .txt file with the following format:
John 23
Mary 56
Kyle 99
Gary 100
...etc. etc.
What we have to do is read the file, and store the names and scores in parallel arrays.
This is turning out to be a bit more challenging to me than I anticipated. What is confusing me, when searching around stack, is all the different libraries people use to do this. Our Prof just wants us to use string, fstream, and sstream to do this.
Below is what I've come up with so far, it compiles perfectly, splits the scores from the names but stores them in the same array:
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
const int SIZE = 50;
string names[SIZE];
int score [SIZE];
short loop = 0;
string line;
ifstream inFile("winners.txt");
if (inFile.is_open())
{
while(!inFile.eof())
{
istream& getline(inFile >> line);
names[loop] = line;
cout << names[loop] << endl;
loop++;
}
inFile.close();
}
else cout << "Can't open the file" << endl;
return 0;
}
I'm not looking for someone to solve my HW problem, I just want a push in the right direction!
If you want to read two things for each line of input, it seems reasonable to have two "read" statements:
std::string name;
inFile >> name;
int score;
inFile >> score;
std::cout << "Read score " << score << " for name " << name << '\n';
...then you can do that repeatedly until you've read the entire file.
Edit: After you get the basic logic worked out, you might want to think about error handling. For example, what is appropriate behavior for your program if the input file doesn't contain 50 pairs of (name, score)? How can you change your code to get that behavior?
Each line in the file consists of a name and a score separated by whitespace. You're reading each line but not splitting it into its parts (the name and the score).
Ideally you would use a vector for this, but since it seems that you were asked to use arrays we'll stick with arrays. What you have above looks good until you start reading entries. A more idiomatic way to accomplish this is to use std::getline, i.e.
ifstream inFile( "winners.txt" );
std::string line;
while( std::getline( inFile, line )) {
// Do work here.
}
Inside the loop you need to split the line on the space. Without solving the problem for you, I suggest you take a look at the find and substr functions of the string class: here. They will give you everything you need to solve the problem.