Having trouble opening a json file in C++ - 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

Related

How to throw exception if file is empty in c++?

in below code i am trying to print message if file is empty and then throwing exception. Consider file Text.txt is empty.
ifstream inputFile;
try
{
inputFile.exceptions(ifstream::eofbit);
inputFile.open("Text.txt");
if (inputFile.is_open()) {
cout << "file opened"<<endl;
}
if (inputFile.peek()== ifstream::traits_type::eof())
{
cout << "file opened but it is empty or invalid
content" << endl;
}
}
catch (ifstream::failure &e)
{
cout << "Some issue with input file: " << e.what()<<endl;
_exit(1);
}
But it is throwing exception before going into the if block (if (inputFile.peek()== ifstream::traits_type::eof())).What I am missing?
This problem happens when the file exists and it's empty.
The problem is that you enabled exceptions upon eof in the line: inputFile.exceptions(ifstream::eofbit);
It opens the file and since it's empty, it immediately sets the eof flag, and you get an exception. If you comment out this line, it will work OK.
Anyway, I would write the same code it a little shorter way:
ifstream inputFile("Text.txt");
if (inputFile)
{
cout << "file opened" << endl;
inputFile.peek();
if (inputFile.eof())
{
cout << "file opened but it is empty or invalid content" << endl;
}
}

Reading and writing to the same file fstream

I would like to update existing json file.
This is example json file:
{
"Foo": 51.32,
"Number": 100,
"Test": "Test1"
}
Logs from program:
Operation successfully performed
100
"Test1"
51.32
46.32
Done
Looks like everythink works as expected...
If I change fstream to ifstream to read and later ofstream to write it's working...
I tried use debugger and as I see I have wrong data in basic_ostream object... but I dont know why, I use data from string with corrected (updated data).
Any idea what is wrong :-) ?
You have a few problems here.
First the command json json_data(fs); reads to the end of the file setting the EOF flag. The stream will stop working until that flag is cleared.
Second the file pointer is at the end of the file. If you want to overwrite the file you need to move back to the beginning again:
if (fs.is_open())
{
json json_data(fs); // reads to end of file
fs.clear(); // clear flag
fs.seekg(0); // move to beginning
Unfortunately that still doesn't fix everything because if the file you write back is smaller than the one you read in there will be some of the old data tagged to the end of the new data:
std::cout << "Operation successfully performed\n";
std::cout << json_data.at("Number") << std::endl;
std::cout << json_data.at("Test") << std::endl;
std::cout << json_data.at("Foo") << std::endl;
json_data.at("Foo") = 4.32; // what if new data is smaller?
Json file:
{
"Foo": 4.32, // this number is smaller than before
"Number": 100,
"Test": "Test1"
}} // whoops trailing character from previous data!!
In this situation I would simply open one file for reading then another for writing, its much less error prone and expresses the intention to overwrite everything.
Something like:
#include "json.hpp"
#include <iostream>
#include <fstream>
#include <string>
using json = nlohmann::json;
void readAndWriteDataToFile(std::string fileName) {
json json_data;
// restrict scope of file object (auto-closing raii)
if(auto fs = std::ifstream(fileName))
{
json_data = json::parse(fs);
std::cout << "Operation successfully performed\n";
std::cout << json_data.at("Number") << std::endl;
std::cout << json_data.at("Test") << std::endl;
std::cout << json_data.at("Foo") << std::endl;
}
else
{
throw std::runtime_error(std::strerror(errno));
}
json_data.at("Foo") = 4.32;
std::cout << json_data.at("Foo") << std::endl;
std::string json_content = json_data.dump(3);
if(auto fs = std::ofstream(fileName))
{
fs.write(json_content.data(), json_content.size());
std::cout << "Done" << std::endl;
}
else
{
throw std::runtime_error(std::strerror(errno));
}
}
int main()
{
try
{
std::string fileName = "C:/new/json1.json";
readAndWriteDataToFile(fileName);
}
catch(std::exception const& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

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.

How to read and write input file and output file

I am trying to run the following program:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile;
ofstream outFile;
double first=1.49, second=2.59, third=3.69, fourth=4.79;
inFile.open("prices.txt");
char response;
if(!inFile.fail())
{
cout << "A file by the name prices.txt exists.\n" << "Do you want to continue and overwrite it\n" << " with the new data (y or n);"; cin >> response;
if(tolower(response) == 'n')
{
cout << "The existing file will not be overwritten." << endl;
return -1;
}
}
outFile.open("prices.txt");
if (inFile.fail())
{
cout << "\n The file does not exist and can not be opened" << endl;
cout << "The file has been successfully opened for output." << endl;
outFile << first << "\n" << second << "\n" << fourth << "\n" << endl;
outFile.close();
exit(1);
cout << "The file has been successfully opened for output. " << endl;
outFile << first << "\n" << second << "\n" << third << "\n" << fourth << endl;
outFile.close();
return 0;
}
}
Yet this program will not write the values to the prices.txt file. If you run the program once it says the file does not exist. Running it a second time says the file is already there and if you want to overwrite it. The thing is searching my Mac I cannot find this file anywhere.
Any ideas what I am doing wrong with running it in Xcode? A friend runs the exact same code in Visual Studio 2008 and it works. Any help is appreciated.
You need to set the working directory for the executable since you are assuming that your data files are in the current working directory. In Xcode 3.x you set this in the first tab of Get Info for the executable. In Xcode 4.x it has been moved, but the principle is the same.
Alternatively you can change your data file paths (e.g. make them absolute) so that you do not make assumptions about the current working directory.
You may not have permission to write into the directory that you are trying to save the file too.
Also, there is an error in your program and I am sure if it is there for debugging reasons. You have
outFile.close();
exit(1);
But then shortly there after you try to write to the file, then close it again.

XCode will not take input from a file

For some reason, Xcode will not take input from a file, while Visual C++ will.
When I run this program in xcode, the variables numberRows and numberCols stay 0 (they are initialized to 0 in the main function).
When I run it in Visual C++ they become 30 and 30 (the top line of maze.txt is "30 30" without the quotes).
Any ideas why this is happening?
void readIn(int &numberRows, int &numberCols, char maze[][100]){
ifstream inData;
inData.open("maze.txt");
if (!inData.is_open()) {
cout << "Could not open file. Aborting...";
return;
}
inData >> numberRows >> numberCols;
cout << numberRows << numberCols;
inData.close();
return;
}
There is something else wrong.
Unfortunately it is hard to tell.
Try flushing the output to make sure you get the error message:
void readIn(int &numberRows, int &numberCols, char maze[][100])
{
ifstream inData("maze.txt");
if (!inData) // Check for all errors.
{
cerr << "Could not open file. Aborting..." << std::endl;
}
else
{
// Check that you got here.
cerr << "File open correctly:" << std::endl;
// inData >> numberRows >> numberCols;
// cout << numberRows << numberCols;
std::string word;
while(inData >> word)
{
std::cout << "GOT:(" << word << ")\n";
}
if (!inData) // Check for all errors.
{
cerr << "Something went wrong" << std::endl;
}
}
}
interesting, so I followed the following suggestion from this post http://forums.macrumors.com/showthread.php?t=796818:
Under Xcode 3.2 when creating a new
project based on stdc++ project
template the target build settings for
Debug configuration adds preprocessor
macros which are incompatible with
gcc-4.2:
_GLIBCXX_DEBUG=1
_GLIBXX_DEBUG_PEDANTIC=1
Destroy them if you want Debug/gcc-4.2
to execute correctly.