Passing stringstream as ostream&, no content being read - c++

I'm coding an assignment where, basically, I have a client and server communicating. For this, I'm using a stringstream in the server side to process the requests (that come in the form of strings) and help build a string response. The server side holds an object (FileManager) that contains several MultimediaFile objects, each with information about themselves. This information can be printed through a method, of which one of the parameters is an ostream&.
To get the request from the client, the stringstream works just fine, but when I pass it to the method, for some reason it doesn't read the content (whereas if I pass std::cout it works just fine). I'm thinking it has something to do with the way stringstream uses the << operator to read information, but I'm unsure how to correct this.
I know I could simply store the information I need about the object in a string or something like that (instead of using ostream&), but the professor wants the method to print directly to the passed ostream, and not return a string.
Here's the code:
bool processRequest(TCPServer::Cnx& cnx, const string& request, string& response)
{
bool changeData = false;
if (request == "delMedias" || request == "delGroups") changeData = true;
TCPServer::Lock lock(cnx, changeData);
cerr << "request: '" << request << "'" << endl;
string operation = "";
string filename = "";
stringstream iss;
iss.str(request); //next few lines get request info, works just fine!
if (iss.rdbuf()->in_avail() != 0)
iss >> operation;
if (iss.rdbuf()->in_avail() != 0)
iss.get();
if (iss.rdbuf()->in_avail() != 0)
getline(iss, filename);
iss.str("");
if (operation.size() == 0 || filename.size() == 0) return false;
response = "";
if (operation.compare("print") == 0)
{
filem.showFile(filename, iss); //Problem here!!!!
if (iss.rdbuf()->in_avail() != 0) //iss is always empty
{
response = iss.str();
response = "Print info: " + response;
}
else
response = "No info received!";
}
else if (operation.compare("play") == 0)
{
filem.playFile(filename);
response = "File played!";
}
else
response = "Operation not valid";
cerr << response << endl;
return true;
}
TCPServer is a class provided by the professor to make things easier, but basically the client sends a string request and, at the end of this function, receives a string response from the server. filem is the FileManager class object, and here's the code for the showFile method:
void FileManager::showFile(string name, ostream& s)
{
if (mfiles.find(name) != mfiles.end())
mfiles[name]->printFile(s);
}
mfiles is a map of string to MultimediaFile* (specifically std::shared_ptr of MultimediaFile, but anyway). Basically, this code checks if there is a file named name, and if so, calls the method printFile(s) where s here would be the stringstream. Here's the method's code:
void MultimediaFile::printFile(ostream& s) const
{
s << "File name: " << name << "\tFilepath: " << filepath << "\n";
}
name and filepath are instance variables of the MultimediaFile class. So, yeah, here I was expecting my stringstream to receive this information, which would then be used to build the response string in the main part of the code, just after the method call. That's not what happens however, and the stringstream is always empty (and since this works with std::cout, then the problem is not the data in the MultimediaFile object).
Again, I would say stringstream behaves differently than cout when getting data through the << operator, but I couldn't find any information that would help me in this case... Does anybody have an idea?
If there's any other information you need please let me know. And thanks in advance!

So, apparently I found a solution.
To test some things, I tried creating a separate bit of code just to check how the method would behave with the stringstream. And... it worked. The only difference between my separate tests, and the problem itself was that, in my program, the stringstream was being used to get data from the request string before being passed to the method to get data from there. So, what I did is, I created a second stringstream that was passed to the method... and it worked.
Basically, this is what I changed (the rest of the code is the same as the original post):
response = "";
stringstream iss2; //create another stringstream
if (operation == "print")
{
filem.showFile(filename, iss2); //use it instead of the first one
if (iss2.rdbuf()->in_avail() != 0)
{
response = iss2.str();
response = "Print info: " + response;
}
else
response = "No info received!";
}
I have no idea why the first stringstream doesn't work; maybe one of the methods I used to get the request information (str(), get(), getline() or the >> operator) change the state of the stringstream so that it doesn't accept new information? I don't know, just random thoughts.
But anyway, this works, so I'm happy for now...
P.S.: I also changed the operation.compare("print") to operation == "print". I couldn't remember the reason I used compare, and I got no warnings during compilation like I thought I had, so, yeah...

Related

Poco::Net::FTPClientSession uploading blank, 0-byte copy of the actual target file

I am currently writing a class which handles a variety of FTP requests and I'm using Poco's FTPClientSession class. I've managed to get most of the stuff I needed to work, however I'm facing an issue regarding uploading files to the server.
int __fastcall upload(String sLocalPath,
String sLocalFile, // String can be substituted by std::string here, basically the same thing
String sRemotePath,
String sRemoteFile,
String& sErr,
int iMode,
bool bRemoveFile)
{
try
{
// replace backslashes with forward slashes in the filepath strings,
// append one if necessary
std::string sLocalFilepath = sLocalPath.c_str();
std::replace(sLocalFilepath.begin(), sLocalFilepath.end(), '\\', '/');
if (sLocalFilepath[sLocalFilepath.size() - 1] != '/')
sLocalFilepath += "/";
sLocalFilepath += sLocalFile.c_str();
std::string sRemoteFilepath = sRemotePath.c_str();
std::replace(sRemoteFilepath.begin(), sRemoteFilepath.end(), '\\', '/');
// traverses and/or creates directories in the server (this definitely works)
FailsafeDirectoryCycler(sRemoteFilepath, "/");
// upload the file
m_Session.beginUpload(sLocalFilepath);
m_Session.endUpload();
// change the name if necessary
if (sLocalFile != sRemoteFile)
{
std::string oldName = sLocalFile.c_str();
std::string newName = sRemoteFile.c_str();
m_Session.rename(oldName, newName);
}
// delete the local file if specified
if (bRemoveFile)
DeleteFileA((sLocalPath + sLocalFile).c_str());
m_Session.setWorkingDirectory("/");
return 0;
}
catch (Poco::Exception& e)
{
std::cout << e.displayText() << std::endl;
return -1;
}
}
The above function also changes the file transfer mode (TYPE_TEXT or TYPE_BINARY), however I excluded it for clarity, as I am certain it works as intended. The call to this function looks as follows:
f.upload(".", "filename123.txt", "testdir1\\testdir2\\abc", "filenamenew.txt", err, 0, true);
The arguments indicate that the file to be transfered is ./filename.txt, and it will end up as /testdir1/testdir2/abc/filenamenew.txt, which is exactly what happens (the rest of the arguments doesn't matter in this case). However, my issue is, the local file contains a short string: abcdef. The file, which gets uploaded to the server, does not contain a single byte; it is blank.
I couldn't find an answer to this question other than there is insufficient space on the server, which is definitely not the issue in this case. I shall add that the server is hosted locally using XLight FTP Server. Has anyone encountered this kind of problem before?
Turns out, Poco::Net::FTPClientSession::beginUpload() doesn't read the file on its own. It returns a reference to an std::ostream, to which you need to load the contents of the file yourself (e.g. using std::ifstream):
std::ifstream hFile(sLocalFilepath, std::ios::in);
std::string line;
std::ostream& os = m_Session.beginUpload(sRemoteFile.c_str());
while (std::getline(hFile, line))
os << line;
hFile.close();
m_Session.endUpload();

C++ Merge 2 Strings Together

I'm trying to create a simple text file to Desktop, but I get in the console : "Access is denied.". I do the same thing from the Comand Line and no error. If I where to print the path I would get :
"
C:/Users/Alex
(Here is a new line)
/Desktop/MyTextFile.txt
I know when I add a string to another string via += string, I get a space between the 2 strings. Any help would be apreciate !
string getClip() {
HANDLE clip;
string clip_text = "";
if (OpenClipboard(NULL))
{
clip = GetClipboardData(CF_TEXT);
clip_text = (char*)clip;
CloseClipboard();
return clip_text;
}
}
string getUser() {
system("echo %username% | clip");
string user = getClip();
return user;
}
void create_a_simple_txt_file() {
string username = getUser();
ostringstream path;
path << "C:/Users/" << username << "/Desktop/MyTextFile.txt";
system(("echo I successful Write something to my file > " + path.str()).c_str());
}
int main() {
create_a_simple_txt_file();
cin.get();
return 0;
}
The problem is the usage of system and the echo command:
system("echo %username% | clip");
The echo command adds a trailing newline, which is copied into the clipboard with the clip command, and you then get the full string including the newline with getClip.
You could simply fix this by not using system at all, and instead get the username with the GetUserName Windows API function:
std::string getUser()
{
char username[64];
// Use the ASCII version of the function
if (GetUserNameA(username, sizeof username))
{
return username;
}
// An error
std::cerr << "Error getting username: " << GetLastError() << '\n';
return "";
}
I would bet your problem is in the getUser() function, that is returning the username with a trailing newline, not the string concatenation (that, by the way, does NOT add a space).
EDITED after question being edited:
As I supposed, the problem is in your getUser() function, see "Some programmer dude" answer about why it is wrong and a possible solution, if you are running on Windows OS.
Another problem can be that you are trying to access the desktop folder of another user, and the user running your program does not have permissions on it.
Another suggestion: you may want to use the appropriate C++ functions or fstream to write to a file, instead of using system().
You are getting an access denied error because you are working on garbage data. The line
system(("echo I successful Write something to my file > " + path.str()).c_str());
creates a temporary string and gets a pointer to its underlying character array. But before the system() call is executed, the temporary string gets destroyed and the pointer becomes invalid.
If you replace your system call with a cout you can see what command you are actually trying to execute. For me it was something like Ó╣iwµ, which is neither a valid command not a valid directory name. Hence, the access denied.
To solve that problem, assign the temporary string to a variable until you have used it:
string s = ("echo I successful Write something to my file > " + path.str());
system(s.c_str());

Reading a string from a file in C++

I'm trying to store strings directly into a file to be read later in C++ (basically for the full scope I'm trying to store an object array with string variables in a file, and those string variables will be read through something like object[0].string). However, everytime I try to read the string variables the system gives me a jumbled up error. The following codes are a basic part of what I'm trying.
#include <iostream>
#include <fstream>
using namespace std;
/*
//this is run first to create the file and store the string
int main(){
string reed;
reed = "sees";
ofstream ofs("filrsee.txt", ios::out|ios::binary);
ofs.write(reinterpret_cast<char*>(&reed), sizeof(reed));
ofs.close();
}*/
//this is run after that to open the file and read the string
int main(){
string ghhh;
ifstream ifs("filrsee.txt", ios::in|ios::binary);
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
cout<<ghhh;
ifs.close();
return 0;
}
The second part is where things go haywire when I try to read it.
Sorry if it's been asked before, I've taken a look around for similar questions but most of them are a bit different from what I'm trying to do or I don't really understand what they're trying to do (still quite new to this).
What am I doing wrong?
You are reading from a file and trying to put the data in the string structure itself, overwriting it, which is plain wrong.
As it can be verified at http://www.cplusplus.com/reference/iostream/istream/read/ , the types you used were wrong, and you know it because you had to force the std::string into a char * using a reinterpret_cast.
C++ Hint: using a reinterpret_cast in C++ is (almost) always a sign you did something wrong.
Why is it so complicated to read a file?
A long time ago, reading a file was easy. In some Basic-like language, you used the function LOAD, and voilà!, you had your file.
So why can't we do it now?
Because you don't know what's in a file.
It could be a string.
It could be a serialized array of structs with raw data dumped from memory.
It could even be a live stream, that is, a file which is appended continuously (a log file, the stdin, whatever).
You could want to read the data word by word
... or line by line...
Or the file is so large it doesn't fit in a string, so you want to read it by parts.
etc..
The more generic solution is to read the file (thus, in C++, a fstream), byte per byte using the function get (see http://www.cplusplus.com/reference/iostream/istream/get/), and do yourself the operation to transform it into the type you expect, and stopping at EOF.
The std::isteam interface have all the functions you need to read the file in different ways (see http://www.cplusplus.com/reference/iostream/istream/), and even then, there is an additional non-member function for the std::string to read a file until a delimiter is found (usually "\n", but it could be anything, see http://www.cplusplus.com/reference/string/getline/)
But I want a "load" function for a std::string!!!
Ok, I get it.
We assume that what you put in the file is the content of a std::string, but keeping it compatible with a C-style string, that is, the \0 character marks the end of the string (if not, we would need to load the file until reaching the EOF).
And we assume you want the whole file content fully loaded once the function loadFile returns.
So, here's the loadFile function:
#include <iostream>
#include <fstream>
#include <string>
bool loadFile(const std::string & p_name, std::string & p_content)
{
// We create the file object, saying I want to read it
std::fstream file(p_name.c_str(), std::fstream::in) ;
// We verify if the file was successfully opened
if(file.is_open())
{
// We use the standard getline function to read the file into
// a std::string, stoping only at "\0"
std::getline(file, p_content, '\0') ;
// We return the success of the operation
return ! file.bad() ;
}
// The file was not successfully opened, so returning false
return false ;
}
If you are using a C++11 enabled compiler, you can add this overloaded function, which will cost you nothing (while in C++03, baring optimizations, it could have cost you a temporary object):
std::string loadFile(const std::string & p_name)
{
std::string content ;
loadFile(p_name, content) ;
return content ;
}
Now, for completeness' sake, I wrote the corresponding saveFile function:
bool saveFile(const std::string & p_name, const std::string & p_content)
{
std::fstream file(p_name.c_str(), std::fstream::out) ;
if(file.is_open())
{
file.write(p_content.c_str(), p_content.length()) ;
return ! file.bad() ;
}
return false ;
}
And here, the "main" I used to test those functions:
int main()
{
const std::string name(".//myFile.txt") ;
const std::string content("AAA BBB CCC\nDDD EEE FFF\n\n") ;
{
const bool success = saveFile(name, content) ;
std::cout << "saveFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n" ;
}
{
std::string myContent ;
const bool success = loadFile(name, myContent) ;
std::cout << "loadFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n"
<< "content is: [" << myContent << "]\n"
<< "content ok is: " << (myContent == content)<< "\n" ;
}
}
More?
If you want to do more than that, then you will need to explore the C++ IOStreams library API, at http://www.cplusplus.com/reference/iostream/
You can't use std::istream::read() to read into a std::string object. What you could do is to determine the size of the file, create a string of suitable size, and read the data into the string's character array:
std::string str;
std::ifstream file("whatever");
std::string::size_type size = determine_size_of(file);
str.resize(size);
file.read(&str[0], size);
The tricky bit is determining the size the string should have. Given that the character sequence may get translated while reading, e.g., because line end sequences are transformed, this pretty much amounts to reading the string in the general case. Thus, I would recommend against doing it this way. Instead, I would read the string using something like this:
std::string str;
std::ifstream file("whatever");
if (std::getline(file, str, '\0')) {
...
}
This works OK for text strings and is about as fast as it gets on most systems. If the file can contain null characters, e.g., because it contains binary data, this doesn't quite work. If this is the case, I'd use an intermediate std::ostringstream:
std::ostringstream out;
std::ifstream file("whatever");
out << file.rdbuf();
std::string str = out.str();
A string object is not a mere char array, the line
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
is probably the root of your problems.
try applying the following changes:
char[BUFF_LEN] ghhh;
....
ifs.read(ghhh, BUFF_LEN);

How to find specific string constant in line and copy the following

I am creating a somewhat weak/vague database (My experience is very little, and please forgive the mess of my code). For this, I create a check everytime my console program starts that checks whether a database (copied to userlist.txt) is created already, if not a new will be created, if the database exists, however, it should all be copied to a 'vector users' (Which is a struct) I have within the class 'userbase' that will then contain all user information.
My userstats struct looks like this,
enum securityLevel {user, moderator, admin};
struct userstats
{
string ID;
string name;
string password;
securityLevel secLev;
};
I will contain all this information from a textfile in this code,
int main()
{
Userbase userbase; // Class to contain userinformation during runtime.
ifstream inFile;
inFile.open("userlist.txt");
if(inFile.good())
{
// ADD DATE OF MODIFICATION
cout << "USERLIST FOUND, READING USERS.\n";
userstats tempBuffer;
int userCount = -1;
int overCount = 0;
while(!inFile.eof())
{
string buffer;
getline(inFile, buffer);
if (buffer == "ID:")
{
userCount++;
if (userCount > overCount)
{
userbase.users.push_back(tempBuffer);
overCount++;
}
tempBuffer.ID = buffer;
cout << "ID"; // Just to see if works
}
else if (buffer == "name:")
{
cout << "name"; // Just to see if works
tempBuffer.name = buffer;
}
else if (buffer == "password:")
{
cout << "password"; // Just to see if works
tempBuffer.password = buffer;
}
}
if (userCount == 0)
{
userbase.users.push_back(tempBuffer);
}
inFile.close();
}
...
What I try to do is to read and analyze every line of the text file. An example of the userlist.txt could be,
created: Sun Apr 15 22:19:44 2012
mod_date: Sun Apr 15 22:19:44 2012
ID:1d
name:admin
password:Admin1
security level:2
(I am aware I do not read "security level" into the program yet)
EDIT: There could also be more users simply following the "security level:x"-line of the preceding user in the list.
Now, if the program reads the line "ID:1d" it should then copy this into the struct and finally I will put it all into the vector userbase.users[i]. This does not seem to work, however. It does not seem to catch on to any of the if-statements. I've gotten this sort of program to work before, so I am very confused what I am doing wrong. I could really use some help with this. Any other kind of criticism of the code is very welcome.
Regards,
Mikkel
None of the if (buffer == ...) will ever be true as the lines always contain the value of the attribute contained on each line as well as the type of the attribute. For example:
ID:1d
when getline() reads this buffer will contain ID:1d so:
if (buffer == "ID:")
will be false. Use string.find() instead:
if (0 == buffer.find("ID:")) // Comparing to zero ensures that the line
{ // starts with "ID:".
// Avoid including the attribute type
// in the value.
tempBuffer.ID.assign(buffer.begin() + 3, buffer.end());
}
As commented by jrok, the while for reading the file is incorrect as no check is made immediately after getline(). Change to:
string buffer;
while(getline(inFile, buffer))
{
...

C++ Socket recv() reads the same string twice (WinSock2)

I'm working on creating a maze game, where two players connect (one acts as host, the other the player). In this, I'm sending XML data as a string using the send() function. (I'm also using a pre-made Socket class, keeping in mind this is for non-profit activities, meaning it doesn't break the copyright.) Keep in mind the client & server are running on Windows 7 using the WinSock2.h package.
The problem I'm encountering is fairly straightforward. I first send the Maze XML file, this reads properly and is able to save the maze in a series of tiles. After this, another XML file is sent, updating the position of the player (and enemy) of the other user's game. However, when I attempt to READ this line, it starts reading from the beginning of the buffer, and it seems as if the buffer isn't being cleared because it starts reading the Maze XML file again.
Is there a way to clear the buffer that recv() uses? I can't think of any other reason why the Maze XML is being read twice, when it isn't being sent twice.
Below is the code that receives the XML, character by character. This is the server's code, the client code just reverses the order of sending/receiving the data. Not sure if that's necessary or relevant.
[code]
while (1) { char r;
switch(recv(s_, &r, 1, 0)) {
case 0: // not connected anymore;
// ... but last line sent
// might not end in \n,
// so return ret anyway.
return ret;
case -1:
return "";
// if (errno == EAGAIN) {
// return ret;
// } else {
// // not connected anymore
// return "";
// }
}
ret += r;
if (r == '<') {
counter = 0;
check = "";
}
check += r;
if (counter == 6 && check.compare(ender) == 0)
{
return ret;
}
//if (r == '\n') return ret;
counter++;
}
[/code]
And this is the code that sends/receives the different XML files.
[code]
Socket* s=in.Accept();
cout << "Accepted a Call from a Client." << endl;
// Here is where we receive the first (Maze) XML File, and
// send our maze as XML
string mazeS = s->ReceiveLineMaze();
TiXmlDocument testDoc;
testDoc.Parse(mazeS.c_str(), 0, TIXML_ENCODING_UTF8);
testDoc.SaveFile("ServerTestDoc.xml");
//testDoc.SaveFile("testXMLFromString.xml");
Tile** theirMaze = readXML(testDoc);
TiXmlDocument theMaze = maze->mazeToXML();
//theMaze.SaveFile("ClientTestWrite.XML");
TiXmlPrinter printer;
theMaze.Accept(&printer);
string toSend = printer.CStr();
cout << toSend << endl;
s->SendLine(toSend);
//RENDER STUFF IN THIS LOOP
bool inOurMaze = false;
while(boolValues->running) {
// This next line is where I want to receive the update on position
// but instead it reads the Maze XML file again, the one I read up
// above
string posReceive = s->ReceiveLineUpdate();
TiXmlDocument theirPos;
theirPos.Parse(posReceive.c_str(), 0, TIXML_ENCODING_UTF8);
... This is where I process the update XML ...
TiXmlDocument updatePos = maze->updatePositionXML();
TiXmlPrinter printerPos;
updatePos.Accept(&printerPos);
string posSend = printer.CStr();
s->SendLine(posSend);
[/code]
Any help is appreciated. If it isn't clear up top, let me summarize.
I first swap an XML file that details the Maze itself. This works fine. Then I attempt to swap the update XML files, that update the player/enemy positions for the other user. But when I attempt to use recv(...), it starts to read the Maze file again, NOT the update file. It's...perplexing.
Oh, and here's the send code (very simple):
[code]
s += '\n';
send(s_,s.c_str(),s.length(),0);
[/code]
where s_ is the socket and s.c_str is the string that needs to be sent (in this case the different XML files).
As #Martin points out, the issue is definitely with the code. Few things to check, looks like the data is read into the buffer "ret" and that's under your control. Is that getting flushed / cleared each time (the code does not make that clear). If this is fine, check the client code to ensure that the data is sent correctly.
Best option is to debug thru your send and receive functions in the IDE, you should be able to spot whats wrong.