I have a function that iterates through a const char * and uses the character to add objects to an instance of std::map if it is one of series of recognized characters.
#define CHARSEQ const char*
void compile(CHARSEQ s) throw (BFCompilationError)
{
std::cout << "#Receive call " << s << std::endl;
for(int i = 0; s[i] != '\0'; i++)
{
if (std::string("<>-+.,[]").find_first_of(s[i]) == std::string::npos)
{
throw BFCompilationError("Unknown operator",*s,i);
}
std::cout << "#Compiling: " << s[i] << std::endl;
std::cout << "#address s " << (void*)s << std::endl;
std::cout << "#var s " << s << std::endl;
controlstack.top().push_back(opmap[s[i]]);
}
}
The character sequence passed is "++++++++++."
For the first three iterations, the print statements display the expected values of '+', '+', and '+', and the value of s continues to be "+++++++++++.". However, on the fourth iteration, s becomes mangled, producing bizarre values such as 'Ð', 'öê', 'cR ', 'œk' and many other character sequences. If the line that throws the exception is removed and the loop is allowed to continue, the value of s does not change after again.
Other functions have access to s but since this is not a multithreaded program I don't see why that would matter. I am not so much confused about why s is changing but why it only changes on the fourth iteration.
I have searched SO and the only post that seems at all relevant is this one but it still doesn't answer my question. (Research has been difficult because searching "const char* changing value" or similar terms just comes up with hundreds of posts about what part of is is const).
Lastly, I know I should probably be using std::string, which I will if no answers come forth, but I would still like to understand this behavior.
EDIT:
Here is the code that calls this function.
CHARSEQ text = load(s);
std::cout << "#Receive load " << text << std::endl;
try
{
compile(text);
}
catch(BFCompilationError& err)
{
std::cerr << "\nError in bf code: caught BFCompilationError #" << err.getIndex() << " in file " << s << ":\n";
std::cerr << text << '\n';
for(int i = 0; i < err.getIndex(); i++)
{
std::cerr << " ";
}
std::cerr << "^\n";
std::cerr << err.what() << err.getProblemChar() << std::endl;
return 1;
}
Where load is:
CHARSEQ load(CHARSEQ fname)
{
std::ifstream infile (fname);
std::string data(""), line;
if (infile.is_open())
{
while(infile.good())
{
std::getline(infile,line);
std::cout << "#loading: "<< line << '\n';
data += line;
}
infile.close();
}
else
{
std::cerr << "Error: unable to open file: " << fname << std::endl;
}
return std::trim(data).c_str();
}
and the file fname is ++++++++++. spread such that there is one character per line.
EDIT 2:
Here is an example of console output:
#loading: +
#loading: +
#loading: +
#loading: +
#loading: +
#loading: +
#loading: +
#loading: +
#loading: +
#loading: +
#loading: .
#Receive load ++++++++++.
#Receive call ++++++++++.
#Compiling: +
#address s 0x7513e4
#var s ++++++++++.
#Compiling: +
#address s 0x7513e4
#var s ++++++++++.
#Compiling: +
#address s 0x7513e4
#var s ++++++++++.
#Compiling:
#address s 0x7513e4
#var s ßu
Error in bf code: caught BFCompilationError #4 in file bf_src/Hello.txt:
ßu
^
Unknown operatorß
Your load function is flawed. The const char* pointer returned by c_str() is valid only until the underlying std::string object exists. But data is a local variable in load and is cleared after return. Its buffer is not overwritten by zeroes but left as it were as free memory. Therefore printing out the value immediately after returning is likely to work but your program may put new values there and the value pointed by your pointer will change.
I suggest to use std::string as the return value of load as a workaround.
Related
Using YAML::Emitter I sometimes get YAML::Emitter.size() to return a bigger size than strlen(YAML::Emitter.c_str()).
e.g.
YAML::Emitter em;
em << something;
if (em.size() > strlen(em.c_str())
{
std::cout << "WOW " << em.size() << " > " << strlen(em.c_str());
}
This creates issues when writing the yaml text to a buffer:
char* buff = malloc(em.size());
memcpy(buff, em.c_str(), em.size());
because the extra characters at the end fail the yaml parser on the other side that reads this buffer.
Thanks!
// Update the server status xml
string filelocation ("/var/www/html/index.xml");
string firstline ("<server>\n");
string secondline ("\t<current>" + msg.getCount() + "</current>\n");
string thirdline ("\t<highest>" + "--" + "</highest>\n");
string fourthline ("\t<status>Online</status>\n")
string finalline ("</server>");
fstream file;
file.open(filelocation);
file.write(firstline + secondline + thirdline + fourthline + finalline);
string updateFlush ("Server Status updated.");
printf("%s\n", updateFlush);
file.close();
Note that msg.getCount() is a function in the same file to get player count from the central server.
Gives out errors about an operands const char*. Something to do with + or -
Thanks
Take a look at the line
string secondline ("\t<current>" + msg.getCount() + "</current>\n");
"\t<current>" is a const char *
msg.getCount() looks like an int or size_t
</current>\n again is a const char *
Adding a const char * to an int or size_t creates a new const char * pointing to a different address.
The same happens in the line
string thirdline ("\t<highest>" + "--" + "</highest>\n");
Here you are adding pointers together. The result is a pointer pointing to a more or less random address.
And in these two lines:
string updateFlush ("Server Status updated.");
printf("%s\n", updateFlush);
You are creating a C++ string-object and trying to print it using a C print function with a format string that requires a char *.
You are mixing C and C++ or stream based I/O with conventional I/O.
In current C++ you should do it this way:
string filelocation ("/var/www/html/index.xml");
fstream file;
file.open(filelocation);
file
<< "<server>\n"
<< "\t<current>" << msg.getCount() << "</current>\n"
<< "\t<highest>" << "--" << "</highest>\n"
<< "\t<status>Online</status>\n"
<< "</server>";
string updateFlush ("Server Status updated.");
cout << updateFlush << std::endl;
file.close();
Or even more readable:
auto file = std::ofstream("/var/www/html/index.xml");
file
<< "<server>" << std::endl
<< "\t<current>" << msg.getCount() << "</current>" << std::endl
<< "\t<highest>" << "--" << "</highest>" << std::endl
<< "\t<status>Online</status>" << std::endl
<< "</server>";
file.close();
std::cout << "Server status updated." << std::endl;
If operating with streams use std::endl to output a newline. It outputs the correct newline for the operation system (CRLF or LF or whatever) and it flushes the stream.
To use std::cout you have to include <iostream> and for std::ofstream include <fstream>.
If you like it short, you could even do this:
std::ofstream("/var/www/html/index.xml")
<< "<server>" << std::endl
<< "\t<current>" << msg.getCount() << "</current>" << std::endl
<< "\t<highest>" << "--" << "</highest>" << std::endl
<< "\t<status>Online</status>" << std::endl
<< "</server>";
std::cout << "Server status updated." << std::endl;
I am looking to find a C++ fstream equivalent function of C fgets. I tried with get function of fstream but did not get what I wanted. The get function does not extract the delim character whereas the fgets function used to extract it. So, I wrote a code to insert this delim character from my code itself. But it is giving strange behaviour. Please see my sample code below;
#include <stdio.h>
#include <fstream>
#include <iostream>
int main(int argc, char **argv)
{
char str[256];
int len = 10;
std::cout << "Using C fgets function" << std::endl;
FILE * file = fopen("C:\\cpp\\write.txt", "r");
if(file == NULL){
std::cout << " Error opening file" << std::endl;
}
int count = 0;
while(!feof(file)){
char *result = fgets(str, len, file);
std::cout << result << std::endl ;
count++;
}
std::cout << "\nCount = " << count << std::endl;
fclose(file);
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.get(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
if(count == 0){
//only new line character encountered
//adding newline character
str[1] = '\0';
str[0] = '\n';
fp.ignore(1, '\n');
//std::cout << fp.get(); //ignore new line character from stream
}
else if(count != (len -1) ){
//adding newline character
str[count + 1] = '\0';
str[count ] = '\n';
//std::cout << fp.get(); //ignore new line character from stream
fp.ignore(1, '\n');
//std::cout << "Adding new line \n";
}
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
return 0;
}
The txt file that I am using is write.txt with following content:
This is a new lines.
Now writing second
line
DONE
If you observe my program, I am using fgets function first and then using the get function on same file. In case of get function, the stream state goes bad.
Can anyone please point me out what is going wrong here?
UPDATED: I am now posting a simplest code which does not work at my end. If I dont care about the delim character for now and just read the entire file 10 characters at a time using getline:
void read_file_getline_no_insert(){
char str[256];
int len =10;
std::cout << "\nREAD_GETLINE_NO_INSERT FUNCITON\n" << std::endl;
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.getline(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
}
int main(int argc, char **argv)
{
read_file_getline_no_insert();
return 0;
}
If wee see the output of above code:
READ_GETLINE_NO_INSERT FUNCITON
Current Count = 9
This is a
Stream State : Good: 0 Fail: 1
Current Count = 0
Stream State : Good: 0 Fail: 1
You would see that the state of stream goes Bad and the fail bit is set. I am unable to understand this behavior.
Rgds
Sapan
std::getline() will read a string from a stream, until it encounters a delimiter (newline by default).
Unlike fgets(), std::getline() discards the delimiter. But, also unlike fgets(), it will read the whole line (available memory permitting) since it works with a std::string rather than a char *. That makes it somewhat easier to use in practice.
All types derived from std::istream (which is the base class for all input streams) also have a member function called getline() which works a little more like fgets() - accepting a char * and a buffer size. It still discards the delimiter though.
The C++-specific options are overloaded functions (i.e. available in more than one version) so you need to read documentation to decide which one is appropriate to your needs.
my program compiles without error and appears to run through all of the steps correctly. It is supposed to make a php call and return data. tcpdump does show the request going out so popen is being executed, but the receiving party never updates.
The only discrepancy I can find, is that the command variable appears to be missing data.
# .trol.o
market max price is 0.00638671 at position 0
php coin.php 155 0.006387
0.00638672
the second line in the output is the command I am sending to popen
cout << command << endl; -> php coin.php 155 0.006387
that number is supposed to be the same as the one under it 0.00638672
The number 6 and the number 2 have been chopped off somehow.
How do I get the correct data into my popen command?
code:
void mngr(){
//vector defs
vector<std::string> buydat;
vector<std::string> markdat;
vector<std::string> pricedat;
vector<std::string> purchaseid;
vector<double> doublePdat;
vector<double> doubleMdat;
doublePdat.reserve(pricedat.size());
doubleMdat.reserve(markdat.size());
char buybuff[BUFSIZ];
char command[70];
char sendbuy[12];
buydat = getmyData();
markdat = getmarketbuyData();
//string match "Buy" and send results to new vector with pricedat.push_back()
for(int b = 2; b < buydat.size(); b+=7){
if ( buydat[b] == "Buy" ) {
pricedat.push_back(buydat[b+1]);
}
}
transform(pricedat.begin(), pricedat.end(), back_inserter(doublePdat), [](string const& val) {return stod(val);});
transform(markdat.begin(), markdat.end(), back_inserter(doubleMdat), [](string const& val) {return stod(val);});
auto biggestMy = std::max_element(std::begin(doublePdat), std::end(doublePdat));
std::cout << "my max price is " << *biggestMy << " at position " << std::distance(std::begin(doublePdat), biggestMy) << std::endl;
auto biggestMark = std::max_element(std::begin(doubleMdat), std::end(doubleMdat));
std::cout << "market max price is " << *biggestMark << " at position " << std::distance(std::begin(doubleMdat), biggestMark) << std::endl;
if (biggestMy > biggestMark){
cout << "Biggest is Mine!" << endl;
}
else if (biggestMy < biggestMark){
//cout << "Biggest is market!";
*biggestMark += 0.00000001;
sprintf(sendbuy,"%f",*biggestMark);
sprintf(command, "php coin.php 155 %s",sendbuy);
FILE *markbuy = popen(command, "r");
if (markbuy == NULL) perror ("Error opening file");
while(fgets(buybuff, sizeof(buybuff), markbuy) != NULL){
size_t h = strlen(buybuff);
//clean '\0' from fgets
if (h && buybuff[h - 1] == '\n') buybuff[h - 1] = '\0';
if (buybuff[0] != '\0') purchaseid.push_back(buybuff);
}
cout << command << endl;
cout << *biggestMark << endl;
}
}
I would try to use long float format instead of float as the type of biggestMark should be evaluated as iterator across doubles. I mean try to change sprintf(sendbuy,"%f",*biggestMark); to sprintf(sendbuy,"%lf",*biggestMark);. Hope this would help.
There are 2 programs below. In the first I have striped out everything but the problem loop and when I do that it works. In the second program (still striped down from the one I am working on) I use the same logic but the sub string called for_loop_buffer never loads.
Basically I am parsing 1 string which holds a record from a cdf file. The for loop buffer is where I build each field and save it to my class. I have been spinning my wheels on this for days any help would be appreciated.
EDIT:
Here are the contents from the file the program is reading:0,Nicole 0,Debbie –
Program 1
/*******************************************************\
* Debug Version 02-b-1 - string to string build *
* *
* Saving a Person Object to file *
* This program will have a class that can save and *
* retrieve itself to and from a flat file in cdf format *
* *
* The program will accept screen input for subsequent *
* Object store call and will allow a print command that *
* will create a file of mailing labels. *
\*******************************************************/
#include <cstdlib>
#include <iostream>
#include <string>
std::string current_person = "Why does this not work? "; // CDF String version of person record from file
std::string for_loop_buffer = " "; // the substring buffer
int counter2 = 0;
int main( )
{
for (int i = 0;i <= 23;++i){
/*****************************************************************************\
* This next line apears to be the problem *
\*****************************************************************************/
for_loop_buffer[counter2] = current_person[i];
std::cout << "DEBUG - Current person " << current_person << std::endl;
std::cout << "DEBUG - current Person element " << current_person[i] << std::endl;
std::cout << "DEBUG - for Loop Buffer " << for_loop_buffer << std::endl;
std::cout << "DEBUG - for Loop Buffer element " << for_loop_buffer[counter2] << std::endl;
std::cout << "DEBUG - for Loop Buffer counter " << counter2 << std::endl;
++counter2;
} // close for
return (0);
}
Program 2
/*******************************************************\
* Debug Version 02 - Read data from a cdf file *
* *
* Saving a Person Object to file *
* This program will have a class that can save and *
* retrieve itself to and from a flat file in cdf format *
* *
* The program will accept screen input for subsequent *
* Object store call and will allow a print command that *
* will create a file of mailing labels. *
\*******************************************************/
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
/***************************************************************\
* Class Definition - person *
* *
* Member functions *
* save_person -- Save the person object to a flat file in CDF *
* get_person -- Retrives a persons data from a CDF flat file *
\***************************************************************/
// Definition of the Class
class person_class {
public:
struct person_contact {
int person_number; // System assigned not on ui
std::string first_name;
};
// Declarations for the method Prototypes
public:
// A Function that retrieves 1 person from file and
// provides it to the user
void get_person();
} person;
/*********************************************************\
* person_class::get_person -- get a data record from file *
* *
\*********************************************************/
inline void person_class::get_person()
{
// Declaration of private variables for this method
std::string ui_first_name; // User Input from Keyboard
person_contact target_person; // Name of the opbect we are filling
std::string current_person; // CDF String version of person record from file
std::cout << "Enter the First Name of the person you are looking for: ";
std::cin >> ui_first_name;
std::cout << std::endl << "Matching Records: " << std::endl;
// Open file
std::ifstream input_file("ntc_db02test.ntc");
std::cout << "DEBUG - I opened the file " << std::endl;
current_person.clear();
while (input_file.good()){
std::cout << "DEBUG - I have entered the while loop " << std::endl;
// Grab the next record from a comma delimited file
getline(input_file,current_person);
std::cout << "DEBUG - I have performed a getline " << std::endl;
std::cout << current_person << std::endl;
// I need a mechanism to build substrings
// Substring Buffer stuff
int buffer_pointer = 0; // pointer to the next position in the substring buffer
std::string for_loop_buffer =" "; // the substring buffer
for_loop_buffer.clear();
int field_index = 0; // Which field is next to load
/*******************************************************************\
* If everything went as planned I have a single CDF person record *
* in the current_person string and now all I have to do is parse *
* it and place each field in the current instance. *
\*******************************************************************/
// load the instance with the record data from the file
for (int i = 0;i < current_person.length();++i){
std::cout << "DEBUG - I am in the for loop " << std::endl;
// look for the end of the CDF field
if(current_person[i] == ','){
// Write the buffer to the next field
std::cout << "DEBUG - I found a comma " << std::endl;
switch (field_index) {
case 0:
stringstream(for_loop_buffer) >> target_person.person_number;
std::cout << "DEBUG - Field Index " << field_index << std::endl;
std::cout << "DEBUG - For Loop Buffer " << for_loop_buffer << std::endl;
std::cout << "DEBUG - Person Number " << target_person.person_number << std::endl;
break;
case 1:
stringstream(for_loop_buffer) >> target_person.first_name;
std::cout << "DEBUG - First Name " << target_person.first_name << std::endl;
break;
default:
std::cout << "This should not happen, index not functioning " << '\n';
break;
} // Close Switch
// clear the buffer
for_loop_buffer.clear(); //SE
buffer_pointer = 0;
// set up the next field load - increment the field index
++field_index; //
}else{ // close if
std::cout << "DEBUG - not a comma " << std::endl;
// If the character is not a comma
// add the character to the buffer
/*****************************************************************************\
* !!!!!!!!!!!! This next line apears to be the problem !!!!!!!!!!!!!!! *
\*****************************************************************************/
for_loop_buffer[buffer_pointer] = current_person[i];
std::cout << "DEBUG - Primary Index i " << i << std::endl;
std::cout << "DEBUG - current Person element " << current_person[i] << std::endl;
std::cout << "DEBUG - Buffer Pointer " << buffer_pointer << std::endl;
std::cout << "DEBUG - for Loop Buffer element " << for_loop_buffer[buffer_pointer] << std::endl;
std::cout << "DEBUG - for Loop Buffer " << for_loop_buffer << std::endl;
// Increment the buffer pointer
++buffer_pointer;
} // close else
} // close for
if (target_person.first_name == ui_first_name){
// Code to print out full record
std::cout << target_person.first_name << " "
<< std::endl;
} // Close if
} // Close While
input_file.close();
} // Close Class
/******************************************************************************\
* The idea this time is to do as little in the main as possible and as much *
* in the class as is appropriate. So rather than read in data and pass it to *
* the method, I will call the method and let it do its own UI reading. *
\******************************************************************************/
int main( )
{
/**********************************************************\
* Ask the user if they want to Create a new person record *
* n or find a record in the database f *
\**********************************************************/
// Set up for user input
char command = 'f';
std::cout << " What do you want to do? New Person (n) or Find Person (f) ";
std::cin >> command;
while (command == 'n' || command == 'f'){
if (command == 'n'){
std::cout << "Do Nothing" << std::endl;
} else if (command == 'f'){
person_class current_person_object;
current_person_object.get_person();
}
command = 'x';
std::cout << "I am Back in Main" << std::endl;
}
std::cout << "You entered something other than a -n- or a -f-, program terminating now" << std::endl;
return (0);
}
You are looping through the string, splitting it up on commas, however there are standard library functions that can simplify this for you. This sounds like homework, so I wont post any code answers, but here are some hints:
std::string::find_first_of will search through a string for you, and tell you where the next comma is.
std::string::substr given a position in the string and a length, will return a part of the string.
Using the above two functions in a loop you could pick out your fields in less code, that's easier to follow.
Alternatively, it would be possible to read the values straight from the stream, as the std::getline function you are using has an extra parameter to specify what character terminates the line (i.e. the comma).
Hard to see through the code, but in your "real" code you say for_loop_buffer.clear();, which is synonymous to for_loop_buffer = "";, and later I think you are accessing a member of the (now empty) string:
for_loop_buffer[buffer_pointer] = current_person[i]; // Illegal access!