sscanf multiple inputs - c++

I have an input string that I need to parse to get anywhere from 4 - 16 variables. Using sscanf I can get the first few variables but the rest show as 0. What do I need to do to get the rest?
Line format: COMMAND [1 - 16 float variables separated by a space]
The line is coming in from a file using something like this:
fgets(line, 20, file);
sscanf(line, "%s", param);
string parString(param);
if(parString == "NEAR"){
sscanf(line, "%s %f", param, &np);
printf("%s %f\n", param, np);
}...
Depending on the first word in the line I know how many variables I need to read in for that case.

This task would be much better done with a std::istringstream because it remembers the position you've read to so far. So you could write code like this
#include <sstream>
std::istringstream input("SPHERE s3 -4 2 -10 2 2 1 0 0 0.5 0 0 1 0 1000");
int var1, var2, var3, var4, var5, var6;
// get first four variables
input >> var1 >> var2 >> var3 >> var4;
if (something or other)
{
// get the next two variables
input >> var5 >> var6;
}
Something like that anyway. I'm sure you get the idea.

One way in C would be to use strtok().
char *p = strtok(str, " ");
while(p)
{
// Store each string
// OR convert it to integer using strtol() if an intger was expected
p = strtok(NULL, " ");
}
The loop will terminate when there are no more values to read from str.

Related

Reading integer then rest of the line as a single string from input

Suppose I'm trying to read from the following input.txt
6 Jonathan Kim Jr
2 Suzie McDonalds
4 Patty
... and I want to store the first integers from every line and the rest of the strings as a string variable. This is what I have tried:
int num;
string name1, name2, name3;
while ( ins >> num >> name1 >> name2 >> name3 )
{
// do things
}
Unfortunately this won't work since line 2 and line 3 only has 2 and 1 strings in a respective order so the loop will terminate at the very first loop.
Is there a way to store the rest of the strings after an integer in a single variable, including the white spaces? For example, string variable name would hold:
"Jonathan Kim Jr" // first loop
"Suzie M" // second loop
"Patty" // third loop
I thought about using getline to achieve this as well, but that would require me to isolate the integers from the string and I was hoping there's a better approach do this. Perhaps using a vector?
By default, the >> operator splits on a space, so you could use that to pull the integer into the variable. You could then use getline to grab the rest of the line, and store that into the variable.
Example (if you are reading from std::cin):
int num;
std::string name;
std::cin >> num;
std::getline(std::cin, name);
Why don't use regular expressions (https://en.wikipedia.org/wiki/Regular_expression) to parse (https://en.cppreference.com/w/cpp/regex) the input? I think an expression something along the line of 2 subexpressions:
'^([0-1]+)\s([A-Za-z0-9_-/s]+)'

read line with different length backwards c++

I have a text file that I need to read data from, and these data will go into various arrays later. The text file looks like:
1 5.154600E-05 1.329887E-02 1.907202E-03 3 -1 8937 8889 1 0 890 1532 1533
2 4.639140E-03 9.845286E-03 1.659781E-02 1 9708 0 0 1617
3 1.329887E-02 1.329887E-02 1.108239E-02 4 8497 5442 0 5711 0 1 1611 1619 889 1618
4 1.030920E-04 5.154600E-05 1.412360E-02 3 -1 6966 6965 1 0 888 1620 1330
5 6.030882E-03 6.546342E-03 1.030920E-04 2 8238 6002 0 0 1622 1621
6 9.484464E-03 5.154600E-05 4.072134E-03 2 6104 5455 0 0 2481 1112
1st, I need to take out specific column (in this case 5th column). The size of lines are not equal, and I couldn't break reading after catching 5th column. cin.ignore didn't help.
2nd, 5th column is always an integer, say N, and then (maybe in a separate function) I need to read the LAST N number of same line and store them into arrays. I have no clue how to do it.
Here is part of code:
while (!myfile.eof())
{
// myfile.ignore (50, '\n'); // This is just a try. 5th col. always ends at 50th charachter of line. It didn't work!
double a,b,c,d,e;
myfile >> a >> b >> c >> d >> e;
if (minsize>e)
minsize=e;
if (maxsize<e)
maxsize=e;
frequency2[int(e)]++;
}
Any help ?
You could use std::getline to get the whole line in each iteration, and then use std::istringstream to parse out the columns. Something like this:
std::string buffer;
std::getline(myfile, buffer);
std::istringstream iss(buffer);
// Do your stuff
double a, b, c, d, e;
iss >> a >> b >> c >> d >> e;
I'd recomming you using a string with some reversed size like 60 bytes.
Then read char by char. If the char is a White space/Line break and the string size > 0 you should handle the data in the string and clear the data in the string. If the size == 0 you should continue.
void handle(std::string& data)
{
if(data.size())
{
//Handle Input
data.clear();
}
}
std::string buf(60);
while(file.isgood())
{
char c = file.get();
if(!file.isgood())
break;
switch(c)
{
case '\n':
case ' '://Probably you need more cases here just debug to find out
handle(buf);
break;
default:
buf.push_back(c);
break;
}
}
To complete others solutions:
getline is the good way to read a file line by line
you could use (boost) tokenizer to build an iterable from the line
you could use (boost) lexical cast to parse data from this iterable
something like
while(file.good())
{
string line;
getline(file, line);
if(line.empty())
continue;
boost::tokenizer<> tok(line);
boost::tokenizer<>::iterator tokiter = tok.begin();
double ex1 = boost::lexical_cast<double>(*tokiter);
tokiter++;
float ex2 = boost::lexical_cast<float>(*tokiter);
//etc..
cout << ex1 << " " << ex2 << endl;
}
http://www.boost.org/doc/libs/1_55_0b1/libs/tokenizer/tokenizer.html
http://www.boost.org/doc/libs/1_55_0/doc/html/boost_lexical_cast.html

reading specific integers from text file

I have a text file that contains this information.
GO
pink colour 60 5 0
pink colour 80 10 0
chocs
red colour 100 15 1
red colour 120 15 1
man
blue colour 140 20 2
fast place
Brown colour 160 20 2
Going in plane
Green colour 280 35 5
Im trying to only extract the first integer of every line. the lines that dont have any integers I can skip.
so i can skip line 1 (Go)
but i need 60 from line 2.
and 80 from line 3.
skip line 4. etc...
but i dont know how. any help is much appreciated. thanks.
You could read the file one character at a time and check if that character is a digit. If it is then continue reading until you hit a character that isn't a digit. Then ignore everything until the character is a newline. It could look something like:
char buff;
std::fstream fin("file", std::fstream::in);
buff = fin.getchar();
// Read until end of file
while (buff != file.EOF)
{
// if the char we read is a digit...
if (isdigit(buff))
{
// Continue to read characters if they are an integer (same number)
while (isdigit(buff))
{
// Store buff in some container
}
// Ignore until we hit the end of that line
fin.ignore(256, '\n');
}
}
pseudocode:
read in a line, quit at EOF
for each line
do a **string::find_first_of** for a digit
if digit not found, go to read the next line
do a **std::stoi** to convert to integer
process the integer
You can do like this:
#include <iostream>
#include <sstream>
using namespace std;
... ...
string str;
while (getline(file, str)) // read each line
{
istringstream iss(str);
int value;
string temp;
if (iss >> temp >> temp >> value) // try to read the value
{
// you got the value here
}
}

How to put strings and integers from file into a multidimensional-array?

Lets say there is a competition, and we have x races, and y contestants.
Each race, every contestant gets a point between 0 and 10.
So there is a file, looking something like this( without the dots, of course):
Chet 10 Katie 8 Mark 3 Rachel 5
Chet 3 Katie 9 Mark 6 Rachel 8
Chet 6 Katie 6 Mark 7 Rachel 0
My problem is that here the strings and integers are alternating, and I dont know how to put them into the multidimensional-array.
Below is how I would do this, if there were only integers in a file. Is it possible to modify this so I can use it with my program?
string x;
int score,row=0,column=0;
int marray[10][10] = {{0}};
string filename;
ifstream fileIN;
cout<<"Type in the name of the file!"<<endl;
cin>> filename;
fileIN.open(filename);
while(fileIN.good()) //reading the data file
{
while(getline(fileIN, x))
{
istringstream stream(x);
column=0;
while(stream >> score)
{
marray[row][column] = score;
column++;
}
row++;
}
}
Array (even multidimensional array) are homogeneous, all the entry must be of the same type. This property let the CPU to compute the address of any entry of the array with a simple displace operation.
In your case you want to store a string and a integer. So you can simple use a struct as entry of your array.
struct Result {
Result() : point(0)
{}
std::string contestantName;
unsigned int point;
};
Result results[10][10];
the rest of the code is really similar of what you have written.

How to parse string with sscanf_s()?

I have the string (45 bytes):
String1 String2000000000001234
I need get three variable from this string:
char * var1="String1";
char * var2="String2";
int var3=1234;
Here 3 part with fixed length (15 bytes). I need save all parts to variable without spaces for strings and without leading zero for integers.
Is this possible with scanf()? How I can to do that?
Not possible with sscanf. First, you need to pass a char array (and size if you use the _s variety) and not just a char pointer (i.e. it needs to have memory associated with it). Second, strings in sscanf terminate by whitespace, and you have none between String2 and 0000. Write a custom parser for this format.
According to this, your problem should be solved by the following format:
%*[^a-zA-Z]%s%*[^a-zA-Z]%[^0]s%*[^1-9]%d
Description:
%*[^a-zA-Z] - skip(do not store) until alphabetical, i.e. skip leading spaces
%s - read first string until spaces
%*[^a-zA-Z] - skip trailing spaces until the next string
%[^0]s - read string util zeros
%*[^1-9] -skip until nonzero digits
%d - finally, read the number
The easiest would probably be:
char var1_buffer[31] = "", var2_buffer[31] = "";
sscanf(string+30, "%d", &var3);
string[30] = '\0';
sscanf(string, "%s%s", var1_buffer, var2_buffer);
var1 = strdup(var1_buffer);
var2 = strdup(var2_buffer);
which requires being able to write to the input string. If that's not possible, you could replace lines 3 and 4 with:
int p1 = 0;
sscanf(string, "%30s %n%30s", var1_buffer, &p1, var2_buffer);
if (p1 > 30)
p1 = 30;
var2_buffer[30-p1] = '\0';
In either case, you should probably add checks of the return value of sscanf to make sure the input string is well-formed.