The exercise says:
Create a Text class that contains a string object to hold the text of
a file. Give it two constructors: a default constructor and a
constructor that takes a string argument that is the name of the file
to open. When the second constructor is used, open the file and read
the contents into the string member object. Add a member function
contents() to return the string so (for example) it can be printed. In
main( ), open a file using Text and print the contents.
This is the class that I wrote:
class Text {
string fcontent;
public:
Text();
Text(string fname);
~Text();
string contents();
};
I haven't understood everything of this exercise. It asks to create a function contents(), that returns a string, but it doesn't says what the function has to do...
Neither what the default constructor has to do.
Could someone help me?
The function has to return the contents of the file, which is stored (in your case) in fcontents.
string Text::contents()
{
return fcontent;
}
The default constructor doesn't have to do anything in this case.
Text::Text(){}
EDIT:
Seeing how many comments there are below with new problems, I'm going to recap and answer the rest of the questions here.
in Text.h you have:
#ifndef TEXT_HH
#define TEXT_HH
#include <string> //[1]
class Text {
std::string fcontent;//[2]
public:
Text();
Text(std::string fname);
~Text();
std::string contents();
};
#endif
and Text.cpp has
// Text.cpp
#include "Text.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
Text::Text() {}
Text::Text(string fname) {
fstream f;
f.open(fname.c_str(), ios::in);//[3]
//[4]
std::stringstream stream;
while(true)
{
char buffer[1000];
f.getline(buffer, 1000);
if(f.good())
{
//This actually adds an extra newline at the end
stream << buffer << '\n';
}
else
{
break;
}
}
fcontent = stream.str();
//remove extra newline
fcontent.erase(fcontent.begin() + fcontent.size() - 1);
f.close();//This is technically unnecessary, but not bad either
}
string Text::contents() {
return fcontent;
}
Text::~Text() {}//[5]
Point 1: The header file <string> contains the class definition for std::string, the C++ string. This should not be confused with <cstring> which contains functions for manipulating C strings (const char *, const char[], etc).
Point 2: The string class exists in the ::std namespace, which means we have to either use std::string every time we want that class or use using namespace std; to pull this class into the global scope. In the header file we prefer the former method because the using declaration doesn't go away, which means that the namespace will be changed for every header and source file that includes this one, which we want to avoid in general (ie. always). In the cpp file however, there is no problem using the using declaration and we do so.
Point 3: fstreams take a C string as the filename parameter, we can get the corresponding C string from a C++ string with the call c_str(). This returns a const char *.
Point 4: To read the whole text file into a string is less obvious than it seems because the way streams deal with eof (end-of-file) and state-checking stuff. In short it will read one more time than you want it to (I know, wanting is subjective, but is close enough I think) before setting the eof flag. That's why the state is checked after calling get and before adding what's been read to our stringstream. Streams are a fairly elaborate topic so I won't go into it in more detail here.
Point 5: Destructors on objects (non-pointers, like our fcontents is) are called automatically, so we don't need to do anything to make sure that our fcontents string is destroyed when our Text object is destroyed. When we allocate something dynamically with new that's when we have to worry about calling delete on it when we want to destroy it.
Related
I'm trying to reproduce (*) something similar to Python fstring, or at least its format function (and while at it, I'd like to implement something like its "Mini-language").
(*) N.B.: please note that I am aware of the existence of the standard lib's format library, as well as the existence of the {fmt} library; but,
a: neither the g++ (11.2.1) nor the clang++ (12.0.1) that I have on my machine can compile code including <format>, and
b: I don't want to use the excellent {fmt} lib, because I'm precisely trying to do my own thing/thingy.
I'm going to use a string in input to my format object, and any number of additional arguments, like that:
// First, some vars
std::string stef{"Stéphane"};
std::string cpp{"C++"};
int ilu3t{3000};
// Then the big deal
std::string my_fstring = badabwe::format(
"My name is {stef}, and I love {cpp} {ilu3t} !",
cpp,
stef,
ilu3t
);
// Obviously, only the 1st parameter is positional!
// my_fstring should now be:
// My name is Stephane, and I love C++ 3000 !
That's one of the first problem, I have to solve. I think this process is called reflection (please let me know if it's the case).
Next I need to handle a variable number of arguments; the 1st parameter is the only positional and mandatory one (I'm still trying to find a way to iterate over a parameter pack), but its a subject for another question.
A function is not aware of name of parameters passed it. The parameter doen't even have to have a name:
void foo(int x); // name of the argument is x
foo(42); // 42 has no name
As suggested in a comment, if you want some mapping between strings (values to be replaced) and strings (their names) then you can use a map. To avoid the caller to spell out this mapping you can use a macro (usually to be avoided, but for now its the only way to get the name of a variable as a string):
#include <iostream>
#include <string>
#include <unordered_map>
using token_t = std::unordered_map<std::string,std::string>;
std::string format(const std::string& tokenized,const token_t& token) {
return "test";
}
#define tokenize(token) { #token , to_string(token) }
using std::to_string;
std::string to_string(const std::string& str) { return str; }
int main() {
std::string stef{"Stéphane"};
std::string cpp{"C++"};
int ilu3t{3000};
std::string my_fstring = format(
"My name is {stef}, and I love {cpp} {ilu3t} !",
{
tokenize(cpp),
tokenize(stef),
tokenize(ilu3t)
}
);
}
I assumed that you can use std::to_string, though there is no std::to_string(const std::string&) hence I added a custom implementation.
I am trying to create a native nodejs module, using NAN and c ++, I want to transform an existing program that uses std::ifstream stream (filename, std :: ifstream :: in | std :: ifstream :: binary); to load a file into a javascript module that can load a buffer and send it to c ++
The original c ++ code was made to work via command line, I don't want to have to write a file to disk, I would like to send this file using a nodejs buffer.
index.js
const fs = require('fs')
const addon = require('./build/Release/image_edit');
fs.readFile('image.png', function read(err, buffer) {
if (err) {
throw err;
}
var result = addon.edit(buffer, buffer.length);
//console.log(result)
});
main.cpp
#include <node.h>
#include <node_buffer.h>
#include <iostream>
#include <nan.h>
#include <sstream>
#include <string>
#include <fstream>
#include <streambuf>
#include <istream>
using namespace Nan;
using namespace v8;
uint32_t read(std::istream& in)
{
uint32_t v;
in.read(reinterpret_cast<char*>(&v), sizeof(v));
return v;
}
NAN_METHOD(edit) {
unsigned char*buffer = (unsigned char*) node::Buffer::Data(info[0]->ToObject());
unsigned int size = info[1]->Uint32Value();
//the closest I could to manipulating the data was using a vector
std::vector<uint32_t> png_data(buffer, buffer + size);
//The main core of the program uses the in.read function to parse the file, tb uses in.clear () and in.seekg ();
//here an example of how this is done
uint32_t count = readU32(stream);
}
NAN_MODULE_INIT(Init) {
Nan::Set(target, New<String>("edit").ToLocalChecked(),
GetFunction(New<FunctionTemplate>(edit)).ToLocalChecked());
}
NODE_MODULE(image_edit, Init)
I tried using the following code to verify that the data received is valid and if the recorded file is the same as the original, everything looks fine.
std::ofstream FILE("test.png", std::ios::out | std::ofstream::binary);
std::copy(png_data.begin(), png_data.end(), std::ostreambuf_iterator<char>(FILE));
The question is, how do I make this buffer received from nodejs into something read the same way an ifstream does, without having to drastically change the c ++ program?
The main methods called by the program in c ++ are: .seekg (), .push_back, .clear (),
This kind of thing is usually done by implementing a custom subclass of std::streambuf, and then using it to construct a std::istream.
std::istream has a constructor that takes a pointer to a std::streambuf as a parameter, so the basic outline is something like this
class my_streambuf : public std::streambuf {
// ... Your implementation of your subclass
};
my_streambuf msb{ /* Parameters to your class's constructor */ }
std::istream i{&msb};
At this point, i is an ordinary input stream and does everything that any other input stream does. You can seek it. You can read from it.
Of course, the hard part is implementing your custom subclass of std::streambuf. This is not something that can be fully described in one or two paragraphs on stackoverflow.com. You should read std::streambuf's documentation, specifically the descriptions of its virtual methods. Your custom subclass will need to reimplement std::streambuf's virtual methods and make them work with your buffer. It's likely you will not need to reimplement all the virtual methods. For some of them their default implementation will be sufficient. Some of them won't be needed, for what you end up doing with std::istream.
You will have to determine, based on you specific needs to what extent you need to reimplement which std::streambuf's virtual methods, and how.
Of course, another, easy alternative is to use your buffer to construct a std::string, and then using it to construct a std::istringstream, and call it a day. Of course, that'll be somewhat wasteful and require effectively doubling the memory used for the data, with a second copy that's owned by a throw-away std::string, and copying it. If this is a small amount of data that's probably fine, but if your buffer is very big that may not be practical, and a custom std::streambuf subclass that uses the buffer directly is your only option.
Like the other answer mentioned, you can use an std::stringstream if you don't want to go the std::streambuf route:
std::stringstream ss;
std::copy(png_data.begin(), png_data.end(), std::ostreambuf_iterator<uint32_t>(ss));
Then you just use it like an input stream.
I have two functions read() and write(). I read a file in the read() function and store a line in the header in a variable. Now i want the write() function to write that same line to a new file. But how can i use the same variable or information from the other function? What is the way to do this?
Here is some info about the code:
After including necessary files, it says this
HX_INIT_CLASS(HxCluster,HxVertexSet);
The name of the class is HxCluster and it would be great if someone can tell me why it is not like we define classes in the simple way: class class_name {};
The I have many functions out of which two are read() and write(). They both take one argument only which is the file to be read and the file to be written to in the respective cases. I don't know if writing the code for that will help here.
If I understood you well, this is just what in C++ the structures/classes/objects are for. For example:
class FileLineWriter
{
public:
FileLineWriter();
void read(istream& inputfile);
void write(ostream& putfile);
private:
string line_of_text;
};
void FileLineWriter::read(istream& s)
{
// s >> this->line_of_text; // possible, but probably will not do what you think
getline(s, this->line_of_text);
}
void FileLineWriter::read(ostream& s)
{
s << this->line_of_text;
}
...
FileLineWriter writer;
writer.read(firstfile);
writer.write(secondfile);
note that the above is NOT a working code. It is just a sample. You will have to fix all typos, missing namespaces, headers, add stream opening/closing/error handling, etc.
You return the variable from read and pass it as a parameter to write. Something like this
std::string read()
{
std::string header = ...
return header;
}
void write(std::string header)
{
...
}
std::string header = read();
write(header);
Passing information between functions is a basic C++ skill to learn.
If I have understood this right then I would suggest that you save the info on the variable to a string or an int depending on what kind of info it is.
I would also recommend to always include some code for us to be able to give you some more help
You can either make write take an argument, void write(std::string text) or you can store the string you read as a global variable std::string text at the top of your .cpp file, text = ... in your read function (replace ... with ifstream or whatever you use) and then write text in your write funcion.
Sure,
Use pointers!
void main(){
char* line = malloc(100*sizeof(char));
read_function (line);
write_function (line);
}
void read_function(char* line){
.... read a line
strcpy (line, the_line_you_read_from_file);
}
void write_function (char* line){
fprintf (fp,"%s", line);
}
I am creating a bank terminal for an assignment. It has the ability to add clients with each client containing 5 different variables for name, address, social#, employer, and income. Those variables are then written to a file once they have been filled and the terminal is exited.
The problem I am having is when starting the terminal I need to read these values from the file, each on their separate lines and store them in their respective variables to use in the addClient() function. This is the code snippet to make things easier than submitted my entire project:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
using namespace std;
std::ifstream infile2("client-info.txt");
//Strings used for respective items from file
string clientName, clientAddress, clientSocial, clientEmployer, clientIncome;
//Here is where I am having the problem of reading the info from the file
//line by line and storing it in respective variables.
while (infile2)
{
getline(infile2,clientName);
getline(infile2,clientAddress);
getline(infile2,clientSocial);
getline(infile2,clientEmployer);
getline(infile2,clientIncome);
client.addClient(clientName, clientAddress, clientSocial, clientEmployer, clientIncome);
}
infile2.close();
}
The file, for example is stored as such.
John Doe
123 Easy Lane
123-45-6789
USSRC
36000
The problem I am having is that I cannot figure out a solid way to get each line and store them in their respective strings. For the assignment, I will not have to be dealing with blank spaces and such. So lines 0-4 will be for one client, 5-9 for another, etc.
A push in the right direction would be greatly appreciated, thanks!
If the addClient function accepts 5 parameters as you have did, then your current main function has already solved your questions.
If you would like to put those 5 strings into a single string, then work this single string inside addClient function.
You can create a class:
class ClientInfo
{
private:
string clientName;
string clientAddress;
string clientSocial;
string clientEmployer,;
string clientIncome;
public:
ClientInfo(string name, string addr, string ssn,
string employer, string income):
clientName(name), clientAddress(addr), clientSocial(ssn),
clientEmployer(employer), clientIncome(income)
{
}
};
Then inside your main, you can do the following:
ClientInfo currentClient(clientName, clientAddress,
clientSocial, clientEmployer, clientIncome);
client.addClient(currentClient);
I think the only problem you're having is when you call getline, you're not passing in a parameter. In this case I think you need to use the newline delimeter.
while (infile2)
{
getline(infile2,clientName, '\n');
getline(infile2,clientAddress, '\n');
getline(infile2,clientSocial, '\n');
getline(infile2,clientEmployer, '\n');
getline(infile2,clientIncome, '\n');
client.addClient(clientName, clientAddress, clientSocial, clientEmployer, clientIncome);
}
I'm not sure on the '\n' syntax but this will read the file until it hits a newline and then move on to the next line.
I have to parse an XML file in C++. I was researching and found the RapidXml library for this.
I have doubts about doc.parse<0>(xml).
Can xml be an .xml file or does it need to be a string or char *?
If I can only use string or char * then I guess I need to read the whole file and store it in a char array and pass the pointer of it to the function?
Is there a way to directly use a file because I would need to change the XML file inside the code also.
If that is not possible in RapidXml then please suggest some other XML libraries in C++.
Thanks!!!
Ashd
RapidXml comes with a class to do this for you, rapidxml::file in the rapidxml_utils.hpp file.
Something like:
#include "rapidxml_utils.hpp"
int main() {
rapidxml::file<> xmlFile("somefile.xml"); // Default template is char
rapidxml::xml_document<> doc;
doc.parse<0>(xmlFile.data());
...
}
Note that the xmlFile object now contains all of the data for the XML, which means that once it goes out of scope and is destroyed the doc variable is no longer safely usable. If you call parse inside of a function, you must somehow retain the xmlFile object in memory (global variable, new, etc) so that the doc remains valid.
New to C++ myself... but I wanted to share a solution.
YMMV!
Shout Out to SiCrane on this thread:
-- and just replacing 'string' with a vector --- (thanks anno)
Please comment and help me learn also! I'm very new to this
Anyway, this seems to work for a good start:
#include <iostream>
#include <fstream>
#include <vector>
#include "../../rapidxml/rapidxml.hpp"
using namespace std;
int main(){
ifstream myfile("sampleconfig.xml");
rapidxml::xml_document<> doc;
/* "Read file into vector<char>" See linked thread above*/
vector<char> buffer((istreambuf_iterator<char>(myfile)), istreambuf_iterator<char>( ));
buffer.push_back('\0');
cout<<&buffer[0]<<endl; /*test the buffer */
doc.parse<0>(&buffer[0]);
cout << "Name of my first node is: " << doc.first_node()->name() << "\n"; /*test the xml_document */
}
We usually read the XML from the disk into a std::string, then make a safe copy of it into a std::vector<char> as demonstrated below:
string input_xml;
string line;
ifstream in("demo.xml");
// read file into input_xml
while(getline(in,line))
input_xml += line;
// make a safe-to-modify copy of input_xml
// (you should never modify the contents of an std::string directly)
vector<char> xml_copy(input_xml.begin(), input_xml.end());
xml_copy.push_back('\0');
// only use xml_copy from here on!
xml_document<> doc;
// we are choosing to parse the XML declaration
// parse_no_data_nodes prevents RapidXML from using the somewhat surprising
// behavior of having both values and data nodes, and having data nodes take
// precedence over values when printing
// >>> note that this will skip parsing of CDATA nodes <<<
doc.parse<parse_declaration_node | parse_no_data_nodes>(&xml_copy[0]);
For a complete source code check:
Read a line from xml file using C++
The manual tells us:
function xml_document::parse
[...] Parses zero-terminated XML string
according to given flags.
RapidXML leaves loading the character data from a file to you. Either read the file into a buffer, like anno suggested or alternatively use some memory mapping technique. (But look up parse_non_destructive flag first.)