C++ ifstream is not reading the entire line from text file - c++

I'm implementing a simple program using text files. Users can register/login accounts that are saved to a txt file.
One of the options is to post a message to a "Post board" that will store the user's name followed by all their posts.
For example:
Leo: Hello
Tony: Bye
I'm able to successfully read the text file and output the message to the console but the username is not displaying, only the message.
For refrence I have a postMessage() function that uses getline() to concatante the username using
ofstream << getUsername() + ": " + string::message.
I have quite a bit of code so I cannot share all of it, however, here are the pieces in question.
public:
char postMessage(User& currentUser, bool loggedIn) { //Will post a message to the board. Passes a User object.
if (loggedIn == false) {
std::cout << "You must be logged in to post!" << std::endl;
userLoginRegisterPrompt();
return 0;
}
char messageState; //Code to verify if message was posted.
std::string message; //Actual message string
std::cout << "Enter your text: " << std::endl;
std::cin.ignore(); //Clears string buffer.
getline(std::cin,message);
std::ofstream post("C:/Users/19097/Desktop/Programs/Registration/Registration/UserPosts/-Posts.txt", std::ios::app); //File with user posts.
post << currentUser.getUserName() << ": " << message << std::endl;
logAction(currentUser.getUserID(), "Made a post");
}
Here is the loadPosts() method to read from the text file and display it to console.
static bool loadPosts() { //Will be for viewing the post history.
std::ifstream post("C:/Users/19097/Desktop/Programs/Registration/Registration/UserPosts/-Posts.txt");
std::string line;
if (!post) {
std::cout << "No board exists!" << std::endl;
return false;
}
while (post >> line) {
getline(post, line);
std::cout << line << std::endl;
}
}
This is what some of the posts read like in the actual txt file.
This is what shows on the console when using loadPosts()

Related

How do I escape user input, if input is a path?

I am writing a simple Mail Server-Client App which is taking a users input for a receiver, a subject line and a message. To store the message, there is a directory in which a subdirectory is being created for every user that receives a message and in the folder each message is being stored with the message index + subject line. e.g. User Bob receives a message with the subject "Hello World", inside the mailspoooler directory a subdirectory with Bob's name is being created and inside the subdirectory a file called '1. Hello World'. The problem with that is, that a user can now enter a path into the subject line and store the information of his message somewhere else, where it's not intended. Is there a way to ecsape the input if someone were to enter a path? Down below you have the code of the function that creates the files. The vector msg is each line of the user input saved and msg[1] is always the subject line
void send(fs::path mailspooler, char* buffer, std::vector<std::string> msg, fs::path current, std::string user){
//switching to mailspooler directory//
try{
fs::current_path(mailspooler);
}
catch(...){
std::cerr << "An error occured with the filesystem" << std::endl;
strcat(buffer, "ERR");
}
//creates subfolder in directory with name of receiver//
fs::create_directory(msg.at(0));
//changing to users directory
try{
fs::current_path(mailspooler.string() + "/" + user);
}
catch(...){
std::cerr << "An error occured with the filesystem" << std::endl;
}
int index = std::distance(fs::directory_iterator(fs::current_path()), {}); // checks to see how many files are already in directory
//create file
std::ofstream user_msg(std::to_string(index + 1) + ". " + msg.at(1));
user_msg << "Sender: " << user << std::endl << "Subject: " << msg.at(1) << std::endl << "Message: " << std::endl;
for(unsigned int i = 2; i<msg.size(); i++)
user_msg << msg.at(i) << std::endl; //writing every single line from the message into file
user_msg.close();
//changing back to base directory
try{
fs::current_path(current);
strcat(buffer, "OK");
}
catch(...){
std::cerr << "An error has occured with the filesystem" << std::endl;
strcat(buffer, "ERR");
}
}

getline function doesn't seem to be working in c++

ifstream read;
read.open(name);
char g[3];
read.getline(g,3);
char v = read.get();
cout << v;
read.close();
the issue i'm having is that after the getline function, the get is set to garbage and the file doesn't read properly anymore. However im sure that the file im reading contains more characters than getline takes, so what is issue?
Did you check that the read worked?
When you use a read always check the read worked before using the value:
if (read.getline(g,3)) {
// Read worked correctly
std::cout << "Got: >" << std::string(g, read.gcount()) << "<\n";
}
else {
std::cerr << "Read Failed\n";
throw "Failed";
}
if ((v = read.get()) != EOF) {
std::cout << "Got: >" << v << "<\n";
}
else {
std::cerr << "Read Failed\n";
throw "Failed";
}
Read file by std::fstream:
std::string str;
std::fstream file = "test.txt";//write in text.txt: GhY67. Test.
while(getline(file, str))//while get line of test.txt file,
// saves
// line in str string.
{
std::cout << str << '\n';
//Outputs str string(all lines in test.txt document)
}
If your wish always input characters, write this code:
std::string str;
while(getline(std::cin, str))//while getline in console, user
//inputs characters and characters in onr line saves in str
//string.*
{
if(str == "What?")
{
std::cout << "Hello, world!";
//*If user inputs string "What?", console outputs
string "Hello, world!*/
}
}

Having trouble opening a json file in C++

I am trying to open a json file that I will be working with in C++. Code that I have used successfully before fails to open the file. I am using Visual Studio 2017 on Windows 10 Pro with JSON for Modern C++ version 3.5.0.
I have a very simple function, which is supposed to open a file as input to a json object. It appears to open the file, but aborts when writing it to the json object. Originally the file to be opened was in another directory, but I moved it into the same directory as the executable while testing...but it didn't help.
Here is the very short function that fails:
json baselineOpenAndRead(string fileName) //passed string used for filename
{
json baseJObject;
cout << "we have a baseJObject" << endl;
//ifstream inFileJSON("test_file.json"); // Making this explicit made no difference
ifstream inFileJSON;
inFileJSON.open("test_file.json", ifstream::in);
cout << "we have opened json inFileJSON" << endl; // get here
inFileJSON >> baseJObject;
cout << " Can direct inFileJSON into baseJObject" << endl; //never get here; the app aborts.
inFileJSON.close();
return baseJObject;
}
This seems basically identical to the example on the nlohmann site:
// read a JSON file
std::ifstream i("file.json");
json j;
i >> j;
I just expected this to open the json file, load it into the object, and return the object. Instead, it just quits.
Thanks for any thoughts...i.e., what am I doing wrong? (I'm going to ignore that it worked before...maybe I missed something).
--Al
As requested, here is a minimal reproducible example, but it will require nlohmann's json.hpp in order to compile:
#include <iostream>
#include <fstream>
#include "json.hpp"
using json = nlohmann::json;
using namespace std;
string fileName;
json baselineOpenAndRead(string);
int main(int argC, char *argV[])
{
json baseJObject;
if (argC != 2) // check to make sure proper number of arguments are given.
{
cout << "\n\nFilename needed...";
exit(1); // number of arguments is wrong - exit program
}
else
{
fileName = argV[1];
baseJObject = baselineOpenAndRead(fileName); // opens and reads the Base Line JSON file
cout << "baseJObject returned" << endl;
}
return 0;
}
json baselineOpenAndRead(string fileName) //
{
cout << "File name: " << fileName << endl;
json baseJObject;
cout << "we have a baseJObject" << endl;
ifstream inFileJSON(fileName);
if (inFileJSON.is_open())
{
cout << "file open..." << endl;
if (nlohmann::json::accept(inFileJSON))
{
cout << "valid json" << endl;
try { inFileJSON >> baseJObject; }
catch (const std::exception &e) { std::cout << e.what() << '\n'; throw; }
}
else
{
cout << "not valid json" << endl;
}
}
else
{
cout << "file not really open" << endl;
}
inFileJSON >> baseJObject;
cout << " We can echo inFileJSON into baseJObject" << endl;
inFileJSON.close();
return baseJObject;
}
I tested it with this json file:
{
"people": [{
"name": "Scott",
"website": "stackabuse.com",
"from": "Nebraska"
},
{
"name": "Larry",
"website": "google.com",
"from": "Michigan"
},
{
"name": "Tim",
"website": "apple.com",
"from": "Alabama"
}
]
}
When I run this passing it the json above as data.json, I get the following output and then it quits:
./Test_json data.json
File name: data.json
we have a baseJObject
file open...
valid json
[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal
Without the try, it just quits. It never gets past inFileJSON >> baseJObject;
Another try that seems to work, but why?
OK. I tried this with the same main (the only changes are in the function):
json baselineOpenAndRead(string fileName) //
{
json baseJObject;
string filePath = "../baselines/" + fileName;
cout << "filePath: " << filePath << endl;
ifstream inFileJSON(fileName);
//baseJObject = json::parse(inFileJSON);
inFileJSON >> baseJObject;
cout << baseJObject << std::endl;
return baseJObject;
}
This looks basically the same to me. I tried making it ifstream inFileJSON(fileName.c_str()) on both the original and in this one. The original continued to fail, this one continued to work. Sorry this is getting so long, but I can't get decent formatting out of comments... Should I just try answering my own question instead?
I think I've got this. I believe my initial problem was caused by an errant ',' in one of my json test files. Subsequently, the if (inFileJSON.is_open) worked, but the if (nlohmann::json::accept(inFileJSON) was failing and causing the same (or perhaps a similar) error. I thought that I needed the c_str() for file paths outside of the executable's directory, but it doesn't seem to make a difference one way or the other. I took out the accept(), and this code seems to work consistently:
json baselineOpenAndRead(string fileName) //
{
json baseJObject;
cout << "we have a baseJObject" << endl;
string filePath = "../baselines/" + fileName;
cout << "filePath: " << filePath << endl;
//ifstream inFileJSON(filePath.c_str());
ifstream inFileJSON(filePath);
if (inFileJSON.is_open())
{
cout << "File is open." << endl;
inFileJSON >> baseJObject;
cout << baseJObject << std::endl;
inFileJSON.close();
return baseJObject;
}
else
{
cout << "File not open." << endl;
exit(1);
}
}
Thanks to everyone for your help. I appreciate it.
--Al

IOS text file is empty after apparently successful writing

IN IOS app, module written in C++ I am writing my data (map of basic strings and integers) to a text file. Using following method:
bool Recognizer::saveMap(const char * s)
{
if(trainingData.model && !trainingData.model.empty()) {
const string filename = string(s);
std::ofstream file(s, ios_base::trunc );
try{
if(! file.is_open())
{
file.open(s);
}
for (map<String,int>::iterator it=trainingData.idMap.begin(); it!=trainingData.idMap.end(); ++it)
{
cout << it->second << " " << it->first << endl;
file << it->first << endl << it->second << endl;
}
file.close();
}
catch(cv::Exception & e){
if(file.is_open())
file.close();
int code = e.code;
string message = e.err;
cerr << "cv::Exeption code: " << code << " " << message << endl;
return false;
}
std::streampos fileLength = iosFileSize(s);
cout << "Saved map to: " << filename << " length: " << fileLength << endl;
return true;
}
return false;
}
My contains one entry and console output indicates that two lines: string, string representing number have been written to my file.
Subsequent opening file for reading and reading using getline or using stream operator indicates that file is empty:
bool Recognizer::loadMap(const char * s)
{
std::streampos fileLenght = iosFileSize(s);
std::ifstream file(s, ios::in);
try{
if(file.is_open())
{
string name;
string lineName;
string lineTag;
int tag;
int count = 0;
while(getline(file,name))
{
if(getline(file,lineTag))
{
tag = stoi(lineTag,0,10);
count++;
cout << tag << " " << name << endl;
trainingData.idMap[name]=tag;
trainingData.namesMap[tag]=name;
}
}trainingData.personsCount=count;
file.close();
}
}
catch(cv::Exception & e){
if(file.is_open())
file.close();
int code = e.code;
string message = e.err;
cerr << "cv::Exeption code: " << code << " " << message << endl;
return false;
}
cout << "Loaded map from: " << s << " lenght: "<< fileLenght << endl;
return true;
}
I also copied from one of stackoverflow answers method returning file lenght and using it to verify lenghth of the file after write operation:
std::streampos iosFileSize( const char* filePath ){
std::streampos fsize = 0;
std::ifstream file( filePath, std::ios::binary );
fsize = file.tellg();
file.seekg( 0, std::ios::end );
fsize = file.tellg() - fsize;
file.close();
return fsize;
}
The file path passed to saveMap and loadMap seems to be legit. With path that the app could not write to, attempt to write caused exception.
There are no errors returned by write operation but both, attempts to read and iosFileSize() indicate that file is empty.
I am not sure if i need call file.open() and file.close() or file is open and closed automatically when output stream is created and later goes out of scope.
I experimented with those with the same result ( call to file.is_open returns true so the block calling file.open() is skipped.
What am I doing wrong?
I appreciate all responses.
It does not seem like you call file.flush(); anywhere in Recognizer::saveMap() after writing to the file stream. std::ofstream::flush() saves changes you've made to the file. Add file.flush(); between when you make changes to the code and when you close the file. See if that remedies your issue.
I also had the same issue. Using file.flush() everytime after you insert to a file can save your file.
However if you insert something like this, say,
file << "Insert This"; You will need to add file.flush().
But some people have issues, like if you just insert file << "Insert This" << endl; , this works fine. The key point here is that, std::endl calls flush() everytime it is used internally. you can say it is a shortend form of "\n" + flush().
I believe from looking at your code that you are overwriting your data when you open the file in the second program you should be using something like this.
std::fstream fs;
fs.open ("test.txt", ios::app)
instead of doing the ios::in

How do I write a message in the Protocol Buffer C++ tutorial?

I am new to Protocol Buffers and inexperienced with C++, I am trying to complete the tutorial at https://developers.google.com/protocol-buffers/docs/cpptutorial
I've created the proto file mentioned in the tutorial and gotten addressbook.pb.h and addressbook.pb.cc from this proto. I am trying to follow the segment "Writing A Message", so I copied and pasted the following code from the tutorial. I immediately run into an issue in the main function, which I'll explain below:
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');
cout << "Enter name: ";
getline(cin, *person->mutable_name());
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}
tutorial::Person::PhoneNumber* phone_number = person->add_phone();
phone_number->set_number(number);
cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
}
// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// Add an address.
PromptForAddress(address_book.add_person());
{
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
In the main function the code exits without prompting for any inputs due to this portion:
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
which appears as one of the first few lines in the function, before any inputs are requested. I read that argc will be the number of inputs. I'm confused because I copied and pasted exactly what the tutorial wrote, but it doesn't appear to be running correctly.
That code expects a file name to be passed on the command line, not read from stdin. You've not specified what platform you're on, but you'll do something like my_program.exe C:\some\file\somewhere on Windows or ./my_program /some/file/somewhere on Linux/Mac/Other Unix like OS. If you're running the program from your IDE's run/debug function then you'll need to configure it to pass the name of the file as a command line argument. How to do that will depend on what IDE you're using.