I am trying to "extract" the name and the next integer. When I run this, I get a run-time error. I have tested without the string and it runs fine.
// Test string
std::string show = "BlahBlah 3";
// Pickup string and int
std::string nameString;
int id;
sscanf(show.c_str(), "%s %i", &nameString, &id);
What am I doing wrong?
sscanf() is a C function, not a C++ function. It has no concept of std::string. You cannot use sscanf() to read into a std::string variable like you have attempted. You need to pre-allocate a char buffer for sscanf() to read into, and then assign that to your std::string, eg:
// Test string
std::string show = "BlahBlah 3";
// Pickup string and int
std::string nameString;
char buffer[32];
int id;
if (sscanf(show.c_str(), "%.31s %i", buffer, &id) == 2)
{
nameString = buffer;
// use values as needed...
}
else
{
// values not parsed...
}
Or, you can preallocate a std::string and have sscanf() fill it:
// Test string
std::string show = "BlahBlah 3";
// Pickup string and int
std::string nameString;
int id;
nameString.resize(32);
if (sscanf(show.c_str(), "%.31s %i", &nameString[0], &id) == 2)
{
nameString.resize(std::strlen(nameString.c_str()));
// use values as needed...
}
else
{
// values not parsed...
}
Since you are using C++, a better option is to use C++ classes to parse the string, eg:
// Test string
std::string show = "BlahBlah 3";
// Pickup string and int
std::string nameString;
int id;
std::istringstream iss(show);
if (iss >> nameString >> id)
{
// use values as needed...
}
else
{
// values not parsed...
}
Well, what you're doing wrong is using sscanf(), a C library function, with std::string, a C++ class. sscanf() does not know anything about C++ classes. sscanf() existed long before C++ was just a glimmer in Stroustrup's eye...
Related
To expand on the title, I am reading a file line-by-line that appears as so:
FirstName,LastName,mm/dd/yyyy,SSN,Role,Salary,Zip,Phone
I have this code I just wrote but am having some trouble placing it into my struct as I'm using std::string as opposed to char[]. I want to continue using std::string for purposes down the road. Also, excuse any syntax errors as I haven't written in c/c++ in a while. I have also read the most elegant way to iterate to words of a string but I am still confused on how to do that with the slashes involved in the date format. SSN and Salary are private members of a struct that will be pushed into a vector for later use. How can I do this using c++ libraries? To be honest, the istringstream confuses me as they include some type of parser inside their struct directly. Is this honestly the best way to accomplish what I'm trying to do?
char stringData[150]; //line to be read in
while(fgets(stringData, 150, infile) != NULL) {
if( currentLine == 1) {
fgets(stringData, 150, infile); //get column names | trash
}
else {
lineSize = sscanf(stringData, "%[^,],%[^,],%d/%d/%d,%d,%[^,],%lf,%[^,],%s", temp.firstName,temp.lastName,
&temp.birthMonth,&temp.birthDay,&temp.birthYear,
&tempSSN, temp.role, &tempSalary, temp.zip,
temp.phoneNum);
if(lineSize != 10) { //error message due to a row being incorrect
cerr << "/* ERROR: WRONG FORMAT OF INPUT(TOO FEW OR TOO MANY ARGUMENTS) ON LINE: */" << currentLine << '\n';
exit(1);
}
temp.setSSN(tempSSN);
temp.setSalary(tempSalary);
vector.push_back(temp);//push Employee temp into the vector and repeat loop
}
currentLine++
}
TL;DR: What is the easiest way to do this using c++ libraries?
As Sam Varshavchik already mentioned, the easiest way would be separating input with , then separate on of them with / again.
Thanks to this famous question I'm using following approach to split string :
template<typename Out>
void split(const std::string &s, char delim, Out result)
{
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim))
{
*(result++) = item;
}
}
std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> elems;
split(s, delim, std::back_inserter(elems));
return elems;
}
assuming that this is your structure :
struct info
{
std::string firstName;
std::string lastName;
std::string birthMonth;
std::string birthDay;
std::string birthYear;
std::string tempSSN;
std::string role;
std::string tempSalary;
std::string zip;
std::string phoneNum;
};
I would implement your needed function like this :
void parser(std::string fileName, std::vector<info> &inf)
{
std::string line;
std::ifstream infile(fileName);
int index = inf.size();
while(std::getline(infile, line))
{
inf.push_back({});
std::vector<std::string> comma_seprated_vec = split(line, ',');
inf.at(index).firstName = comma_seprated_vec.at(0);
inf.at(index).lastName = comma_seprated_vec.at(1);
inf.at(index).tempSSN = comma_seprated_vec.at(3);
inf.at(index).role = comma_seprated_vec.at(4);
inf.at(index).tempSalary = comma_seprated_vec.at(5);
inf.at(index).zip = comma_seprated_vec.at(6);
inf.at(index).phoneNum = comma_seprated_vec.at(7);
std::vector<std::string> slash_seprated_vec = split(comma_seprated_vec.at(2), '/');
inf.at(index).birthMonth = slash_seprated_vec.at(0);
inf.at(index).birthDay = slash_seprated_vec.at(1);
inf.at(index).birthYear = slash_seprated_vec.at(2);
++index;
}
}
Then you can use it like this :
int main()
{
std::vector<info> information;
parser("some file", information);
return 0;
}
There you go, your information are presented in information variable.
I've populated a string vector with with numbers and characters (*,+,-,/). I want to assign each number and character to two new vector, and int vector and a char vector. Is there a way to convert the everything from string to the desired data type?
You can use string stream in the <sstream> header.
string myString = "123";
stringstream sStream( myString );
int convertedInt;
sStream >> convertedInt.
Include the <sstream> header and you can do something like this:
std::vector<std::string> stringVector = /* get data from somewhere */
std::vector<int> intVector;
std::vector<char> charVector;
for (std::vector<std::string>::const_iterator it = stringVector.begin(); it != stringVector.end(); it++)
{
if (it->length() == 0)
continue; // ignore any empty strings
int intValue;
std::istingstream ss(*it);
if (ss >> someValue) // try to parse string as integer
intVector.push_back(someValue); // int parsed successfully
else
charVector.pushBack((*it)[0]);
}
This assumes anything that cannot be parsed as an integer should be pushed into the char vector instead (so, 234, 100000 and -34 will be put into intVector, and /, + etc will be put into charVector). Only the first character of a non-integer value is pushed, so if you have *hello or *123, only * will be put into the charVector.
If you are using C++11, you can swap the std::vector<std::string>::const_iterator with auto to make it look a bit nicer.
Please help me, I have been trying to do this for the past two-three hours, all with no luck. I have a number of strings comming in form input.txt in the format
string1 string2
string3 string4
etc.
that I want to put into a std::set which is initially empty. I want to number the strings as they come in and put them into the set to keep track of the duplicates so I don't number them again. I am trying to initialize std::set<std::string> inGraph but can't make it work. I tried to initialize std::set<std::string> inGraph(0, tot_lines); where 0 to tot_lines is the range of the number of total strings I expect to get form the input. The I tried to initialize all with empty stirng like: std::set<std::string> inGraph(tot_lines, ""); and that failed. Here's what I have now:
struct StringInt {
std::string name; // associate name and number for each input string
int number;
};
int main(int argc, char* argv[]) {
int tot_lines = 100;
int icv1, icv2;
std::string vert1, vert2;
std::set<std::string> inGraph(); // this is the set I want to initialize
std::set<std::string>::iterator sit;
std::vector<StringInt> stringInts(tot_lines*2);
StringInt* si;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2 >> vert1 >> vert2) {
// read in input, put it in vars below
myfile2 >> vert1 >> vert2;
if (inGraph.find(vert1) != inGraph.end()) {
icv1 = i++;
si->name = vert1;
si->number = icv1;
inGraph.insert(vert1);
stringInts.push_back(*si);
}
else {
icv1 = si->number;
}
if (inGraph.find(vert2) != inGraph.end()) {
icv2 = i++;
si->name = vert1;
si->number = icv2;
inGraph.insert(vert2);
stringInts.push_back(*si);
}
else {
icv2 = si->number;
}
}
The error I get is: left of '.find' must have class/struct/union Can you please help me figure out how to initialize the std::set<std::string> inGraph so I can number the strings?
The error message is because you are a victim of Most Vexing Parse.
std::set<std::string> inGraph();
It is a function declaration whose return type is std::set<std::string>. Just remove the () after inGraph to make it a object declaration.
I want to read a long number from file then increment it and write it back to file.
I am struggling with the convertion from string to long and back again.
I tried:
double id = atof("12345678901"); //using atof because numbers are too big for atio()
id++;
ostringstream strs;
strs << static_cast<long>((static_cast<double>(threadId)));
string output = strcpy_s(config->m_threadId, 20, strs.str().c_str());
But that converts the input to a negative or wrong number.
atoi is for normal integers. There's also atol and atoll (_atoi64 in windows):
//long long id = atoll( "12345678901" );
long long id = _atoi64("12345678901"); // for Visual Studio 2010
id++;
// write back to file here
As suggested by one commenter, use strtoll instead of the ato* functions:
char * data = "12345678901";
long long id = strtoull( data, NULL, 10 );
id++;
Since you're using C++ here, you should just pull it straight from the fstreams:
long long id;
{
std::ifstream in( "numberfile.txt" );
in >> id;
}
id++;
{
std::ofstream out( "numberfile.txt" );
out << id;
}
To go from a C string (char array), use this:
long id = atol("12345678901");
Now you can increment the number. Then, to go from a long to a C++ std::string, use this:
std::ostringstream oss;
oss << id;
std::string idAsStr = oss.str();
Now you can write the string back to the file.
Do you have access to Boost.Lexical_Cast? You could simply do the conversion like this:
double id = boost::lexical_cast<double>("some string");
++id
std::string id_string = boost::lexical_cast<std::string>(id);
and use whatever file transfer you currently have.
I want to add a new (fstream) function in a program that already uses char arrays to process strings.
The problem is that the below code yields strings, and the only way i can think of getting this to work would be to have an intermediary function that would copy the strings, char by char, into a new char array, pass these on to the functions in the program, get back the results and then copy the results char by char back into the string.
Surely (hopefully) there must be a better way?
Thanks!
void translateStream(ifstream &input, ostream& cout) {
string inputStr;
string translated;
getline(input, inputStr, ' ');
while (!input.eof()) {
translateWord(inputStr, translated);
cout << translated;
getline(input, inputStr, ' ');
}
cout << inputStr;
the translateWord func:
void translateWord(char orig[], char pig[]) {
bool dropCap = false;
int len = strlen(orig)-1;
int firstVowel = findFirstVowel(orig);
char tempStr[len];
strcpy(pig, orig);
if (isdigit(orig[0])) return;
//remember if dropped cap
if (isupper(orig[0])) dropCap = true;
if (firstVowel == -1) {
strcat(pig, "ay");
// return;
}
if (isVowel(orig[0], 0, len)) {
strcat(pig, "way");
// return;
} else {
splitString(pig,tempStr,firstVowel);
strcat(tempStr, pig);
strcat(tempStr, "ay");
strcpy(pig,tempStr);
}
if (dropCap) {
pig[0] = toupper(pig[0]);
}
}
You can pass a string as the first parameter to translateWord by making the first parameter a const char *. Then you call the function with inputStr.c_str() as the first parameter. Do deal with the second (output) parameter though, you need to either completely re-write translateWord to use std::string (the best solution, IMHO), or pass a suitably sized array of char as the second parameter.
Also, what you have posted is not actually C++ - for example:
char tempStr[len];
is not supported by C++ - it is an extension of g++, taken from C99.
You can use the member function ifstream::getline. It takes a char* buffer as the first parameter, and a size argument as the second.