"Ends_with" function while reading an input file is not working - c++

I am trying to create a C++ code that using boost libraries reads an input file like the following,
1 12 13 0 0 1 0 INLE
.
.
.
In this case, I must do an action if the condition specified on the last column of the right is INLE.
I have the following code,
#include <iostream>
#include <fstream>
#include <string>
#include <boost/algorithm/string/predicate.hpp>
int main(int argc, const char * argv[])
{
std::string line;
const std::string B_condition = "INLE";
std::ifstream myfile ("ramp.bnd");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
if (boost::algorithm::ends_with(line,B_condition)==true)
{
std::cout << "Its True! \n"; // just for testing
//add complete code
}
}
myfile.close();
}
else std::cout << "Unable to open file";
return 0;
}
while compiling there are no issues, but when I run, it doesnt shows anything.
By the other side, if I modify my boolean condition to false, it will print "Its true!" the number of lines that my input file has.
What am I doing wrong?
Thanks!!

I can only assume that:
your file contains whitespace at the end (use trim)
your file has windows line ends (CRLF) but you're reading it as UNIX text files, meaning that the lines will include a trailing `\r' (CR) (often shown as ^M in various text editors/pagers).
So, either
fix the line endings
trim whitespace from the lines before comparing
or both
Best: use a 'proper' parser to do the work.
Update adding a quick & dirty approach using Boost Spirit: see it Live On Coliru
int main()
{
std::ifstream myfile("ramp.bnd");
myfile.unsetf(std::ios::skipws);
boost::spirit::istream_iterator f(myfile), l;
using namespace qi;
bool ok = phrase_parse(f, l,
(repeat(7) [ int_ ] >> as_string[lexeme[+(char_ - eol)]])
[ phx::bind(process_line, _1, _2) ]
% eol, // supports CRLF and LF
blank);
if (!ok)
std::cerr << "Parse errors\n";
if (f!=l)
std::cerr << "Remaing input: '" << std::string(f,l) << "'\n";
}
As you can see, it validates the whole line, assuming (for now) that the columns are 7 integer values and a string (e.g. "INLE"). Now, the actual work is much simpler and can be implemented in a separate function:
void process_line(std::vector<int> const& values, std::string const& kind)
{
if (kind == "INLE")
{
std::cout << "Column 1: " << values[0] << "\n";
}
}
The actual processing function doesn't have to meddle with trimming, line ends, even parsing the details columns :)
Full Code for reference
#include <iostream>
#include <fstream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
static const std::string B_condition = "INLE";
void process_line(std::vector<int> const& values, std::string const& kind)
{
if (kind == "INLE")
{
std::cout << "Column 1: " << values[0] << "\n";
}
}
int main()
{
std::ifstream myfile("ramp.bnd");
myfile.unsetf(std::ios::skipws);
boost::spirit::istream_iterator f(myfile), l;
using namespace qi;
bool ok = phrase_parse(f, l,
(repeat(7) [ int_ ] >> as_string[lexeme[+(char_ - eol)]])
[ phx::bind(process_line, _1, _2) ]
% eol, // supports CRLF and LF
blank);
if (!ok)
std::cerr << "Parse errors\n";
if (f!=l)
std::cerr << "Remaing input: '" << std::string(f,l) << "'\n";
}

You don't need a library like boost at all. A solution with pur standard C++ is possible in some lines of code too:
const std::string B_condition = "INLE";
std::ifstream myfile ("ramp.bnd");
for( char c; myfile >> c; )
{
if( std::isdigit(c, myfile.getloc() ) ) // needs #include <locale>
{
int i;
if( myfile.putback(c) >> i )
std::cout << "read " << i << std::endl; // do something with 'i'
}
else
{
std::string token;
if( myfile.putback(c) >> token )
{
if( token == B_condition )
std::cout << B_condition << " found\n";
else
; // no number, no B_condition -> what ever You want to do
}
}
}

Related

Why does getline() cut off CSV Input?

I'm trying to read and parse my CSV files in C++ and ran into an error.
The CSV has 1-1000 rows and always 8 columns.
Generally what i would like to do is read the csv and output only lines that match a filter criteria. For example column 2 is timestamp and only in a specific time range.
My problem is that my program cuts off some lines.
At the point where the data is in the string record variable its not cutoff. As soon as I push it into the map of int/vector its cutoff. Am I doing something wrong here?
Could someone help me identify what the problem truly is or maybe even give me a better way to do this?
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <map>
#include "csv.h"
using std::cout; using std::cerr;
using std::endl; using std::string;
using std::ifstream; using std::ostringstream;
using std::istringstream;
string readFileIntoString(const string& path) {
auto ss = ostringstream{};
ifstream input_file(path);
if (!input_file.is_open()) {
cerr << "Could not open the file - '"
<< path << "'" << endl;
exit(EXIT_FAILURE);
}
ss << input_file.rdbuf();
return ss.str();
}
int main()
{
int filterID = 3;
int filterIDIndex = filterID;
string filter = "System";
/*Filter ID's:
0 Record ID
1 TimeStamp
2 UTC
3 UserID
4 ObjectID
5 Description
6 Comment
7 Checksum
*/
string filename("C:/Storage Card SD/Audit.csv");
string file_contents;
std::map<int, std::vector<string>> csv_contents;
char delimiter = ',';
file_contents = readFileIntoString(filename);
istringstream sstream(file_contents);
std::vector<string> items;
string record;
int counter = 0;
while (std::getline(sstream, record)) {
istringstream line(record);
while (std::getline(line, record, delimiter)) {
items.push_back(record);
cout << record << endl;
}
csv_contents[counter] = items;
//cout << csv_contents[counter][0] << endl;
items.clear();
counter += 1;
}
I can't see a reason why you data is being cropped, but I have refactored you code slightly and using this it might be easier for you to debug the problem, if it doesn't just disappear on its own.
int main()
{
string path("D:/Audit.csv");
ifstream input_file(path);
if (!input_file.is_open())
{
cerr << "Could not open the file - '" << path << "'" << endl;
exit(EXIT_FAILURE);
}
std::map<int, std::vector<string>> csv_contents;
std::vector<string> items;
string record;
char delimiter = ';';
int counter = 0;
while (std::getline(input_file, record))
{
istringstream line(record);
while (std::getline(line, record, delimiter))
{
items.push_back(record);
cout << record << endl;
}
csv_contents[counter] = items;
items.clear();
++counter;
}
return counter;
}
I have tried your code and (after fixing the delimiter) had no problems, but I only had three lines of data, so if it is a memory issue it would have been unlikely to show.

c++ : istream_iterator skip spaces but not newline

Suppose I have
istringstream input("x = 42\n"s);
I'd like to iterate over this stream using std::istream_iterator<std::string>
int main() {
std::istringstream input("x = 42\n");
std::istream_iterator<std::string> iter(input);
for (; iter != std::istream_iterator<std::string>(); iter++) {
std::cout << *iter << std::endl;
}
}
I get the following output as expected:
x
=
42
Is it possible to have the same iteration skipping spaces but not a newline symbol? So I'd like to have
x
=
42
\n
std::istream_iterator isn't really the right tool for this job, because it doesn't let you specify the delimiter character to use. Instead, use std::getline, which does. Then check for the newline manually and strip it off if found:
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::istringstream input("x = 42\n");
std::string s;
while (getline (input, s, ' '))
{
bool have_newline = !s.empty () && s.back () == '\n';
if (have_newline)
s.pop_back ();
std::cout << "\"" << s << "\"" << std::endl;
if (have_newline)
std::cout << "\"\n\"" << std::endl;
}
}
Output:
"x"
"="
"42"
"
"
If you can use boost use this:
boost::algorithm::split_regex(cont, str, boost::regex("\s"));
where "cont" can be the result container and "str" is your input string.
https://www.boost.org/doc/libs/1_76_0/doc/html/boost/algorithm/split_regex.html

Read and print a csv file with more than 2 column in c++ using multimap

I'm a beginner in c++ and required to write a c++ program to read and print a csv file like this.
DateTime,value1,value2
12/07/16 13:00,3.60,50000
14/07/16 20:00,4.55,3000
May I know how can I proceed with the programming?
I manage to get the date only via a simple multimap code.
I spent some time to make almost (read notice at the end) exact solution for you.
I assume that your program is a console application that receives the original csv-file name as a command line argument.
So see the following code and make required changes if you like:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
std::vector<std::string> getLineFromCSV(std::istream& str, std::map<int, int>& widthMap)
{
std::vector<std::string> result;
std::string line;
std::getline(str, line);
std::stringstream lineStream(line);
std::string cell;
int cellCnt = 0;
while (std::getline(lineStream, cell, ','))
{
result.push_back(cell);
int width = cell.length();
if (width > widthMap[cellCnt])
widthMap[cellCnt] = width;
cellCnt++;
}
return result;
}
int main(int argc, char * argv[])
{
std::vector<std::vector<std::string>> result; // table with data
std::map<int, int> columnWidths; // map to store maximum length (value) of a string in the column (key)
std::ifstream inpfile;
// check file name in the argv[1]
if (argc > 1)
{
inpfile.open(argv[1]);
if (!inpfile.is_open())
{
std::cout << "File " << argv[1] << " cannot be read!" << std::endl;
return 1;
}
}
else
{
std::cout << "Run progran as: " << argv[0] << " input_file.csv" << std::endl;
return 2;
}
// read from file stream line by line
while (inpfile.good())
{
result.push_back(getLineFromCSV(inpfile, columnWidths));
}
// close the file
inpfile.close();
// output the results
std::cout << "Content of the file:" << std::endl;
for (std::vector<std::vector<std::string>>::iterator i = result.begin(); i != result.end(); i++)
{
int rawLen = i->size();
for (int j = 0; j < rawLen; j++)
{
std::cout.width(columnWidths[j]);
std::cout << (*i)[j] << " | ";
}
std::cout << std::endl;
}
return 0;
}
NOTE: Your task is just to replace a vector of vectors (type std::vector<std::vector<std::string>> that are used for result) to a multimap (I hope you understand what should be a key in your solution)
Of course, there are lots of possible solutions for that task (if you open this question and look through the answers you will understand this).
First of all, I propose to consider the following example and to try make your task in the simplest way:
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string str = "12/07/16 13:00,3.60,50000";
stringstream ss(str);
vector<string> singleRow;
char ch;
string s = "";
while (ss >> ch)
{
s += ch;
if (ss.peek() == ',' || ss.peek() == EOF )
{
ss.ignore();
singleRow.push_back(s);
s.clear();
}
}
for (vector<string>::iterator i = singleRow.begin(); i != singleRow.end(); i++)
cout << *i << endl;
return 0;
}
I think it can be useful for you.

How to expand environment variables in .ini files using Boost

I have a INI file like
[Section1]
Value1 = /home/%USER%/Desktop
Value2 = /home/%USER%/%SOME_ENV%/Test
and want to parse it using Boost. I tried using Boost property_tree like
boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini("config.ini", pt);
std::cout << pt.get<std::string>("Section1.Value1") << std::endl;
std::cout << pt.get<std::string>("Section1.Value2") << std::endl;
But it didn't expand the environment variable. Output looks like
/home/%USER%/Desktop
/home/%USER%/%SOME_ENV%/Test
I was expecting something like
/home/Maverick/Desktop
/home/Maverick/Doc/Test
I am not sure if it is even possible with boost property_tree.
I would appreciate any hint to parse this kind of file using boost.
And here's another take on it, using the old crafts:
not requiring Spirit, or indeed Boost
not hardwiring the interface to std::string (instead allowing any combination of input iterators and output iterator)
handling %% "properly" as a single % 1
The essence:
#include <string>
#include <algorithm>
static std::string safe_getenv(std::string const& macro) {
auto var = getenv(macro.c_str());
return var? var : macro;
}
template <typename It, typename Out>
Out expand_env(It f, It l, Out o)
{
bool in_var = false;
std::string accum;
while (f!=l)
{
switch(auto ch = *f++)
{
case '%':
if (in_var || (*f!='%'))
{
in_var = !in_var;
if (in_var)
accum.clear();
else
{
accum = safe_getenv(accum);
o = std::copy(begin(accum), end(accum), o);
}
break;
} else
++f; // %% -> %
default:
if (in_var)
accum += ch;
else
*o++ = ch;
}
}
return o;
}
#include <iterator>
std::string expand_env(std::string const& input)
{
std::string result;
expand_env(begin(input), end(input), std::back_inserter(result));
return result;
}
#include <iostream>
#include <sstream>
#include <list>
int main()
{
// same use case as first answer, show `%%` escape
std::cout << "'" << expand_env("Greeti%%ng is %HOME% world!") << "'\n";
// can be done streaming, to any container
std::istringstream iss("Greeti%%ng is %HOME% world!");
std::list<char> some_target;
std::istreambuf_iterator<char> f(iss), l;
expand_env(f, l, std::back_inserter(some_target));
std::cout << "Streaming results: '" << std::string(begin(some_target), end(some_target)) << "'\n";
// some more edge cases uses to validate the algorithm (note `%%` doesn't
// act as escape if the first ends a 'pending' variable)
std::cout << "'" << expand_env("") << "'\n";
std::cout << "'" << expand_env("%HOME%") << "'\n";
std::cout << "'" << expand_env(" %HOME%") << "'\n";
std::cout << "'" << expand_env("%HOME% ") << "'\n";
std::cout << "'" << expand_env("%HOME%%HOME%") << "'\n";
std::cout << "'" << expand_env(" %HOME%%HOME% ") << "'\n";
std::cout << "'" << expand_env(" %HOME% %HOME% ") << "'\n";
}
Which, on my box, prints:
'Greeti%ng is /home/sehe world!'
Streaming results: 'Greeti%ng is /home/sehe world!'
''
'/home/sehe'
' /home/sehe'
'/home/sehe '
'/home/sehe/home/sehe'
' /home/sehe/home/sehe '
' /home/sehe /home/sehe '
1 Of course, "properly" is subjective. At the very least, I think this
would be useful (how else would you configure a value legitimitely containing %?)
is how cmd.exe does it on Windows
I'm pretty sure that this could be done trivally (see my newer answer) using a handwritten parser, but I'm personally a fan of Spirit:
grammar %= (*~char_("%")) % as_string ["%" >> +~char_("%") >> "%"]
[ _val += phx::bind(safe_getenv, _1) ];
Meaning:
take all non-% chars, if any
then take any word from inside %s and pass it through safe_getenv before appending
Now, safe_getenv is a trivial wrapper:
static std::string safe_getenv(std::string const& macro) {
auto var = getenv(macro.c_str());
return var? var : macro;
}
Here's a complete minimal implementation:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
static std::string safe_getenv(std::string const& macro) {
auto var = getenv(macro.c_str());
return var? var : macro;
}
std::string expand_env(std::string const& input)
{
using namespace boost::spirit::qi;
using boost::phoenix::bind;
static const rule<std::string::const_iterator, std::string()> compiled =
*(~char_("%")) [ _val+=_1 ]
% as_string ["%" >> +~char_("%") >> "%"] [ _val += bind(safe_getenv, _1) ];
std::string::const_iterator f(input.begin()), l(input.end());
std::string result;
parse(f, l, compiled, result);
return result;
}
int main()
{
std::cout << expand_env("Greeting is %HOME% world!\n");
}
This prints
Greeting is /home/sehe world!
on my box
Notes
this is not optimized (well, not beyond compiling the rule once)
replace_regex_copy would do as nicely and more efficient (?)
see this answer for a slightly more involved 'expansion' engine: Compiling a simple parser with Boost.Spirit
using output iterator instead of std::string for accumulation
allowing nested variables
allowing escapes

C++ text file based database read

I need simple program in C++ that will open text file and going line by line find second argument by knowing first in list.
Content of text file:
1, red
2, blue
3, green
4, orange
So I need a program that will go line by line and each line turn into array ( of two elements ) and then compare first element with user interactively inserted number.
So if user insert 2, it goes line by line, comparing first element of array-ed line and if it matches, it prints second element in array ( blue ), and if user type 3, it prints green...
I have always working in PHP, and it's much easier than this, so I am stuck a little bit with this now... :/
Here you go:
#include <iostream>
#include <fstream>
#include <map>
#include <sstream>
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: test [inputfile]" << std::endl;
return 1;
}
std::fstream stream(argv[1]);
if (!stream.good()) {
std::cerr << "Error: could not open file: " << argv[1] << std::endl;
return 2;
}
std::string line;
std::map<int, std::string> map;
while (std::getline(stream, line)) {
std::string::size_type pos = line.find(',');
std::stringstream sstream(line.substr(0, pos));
int index;
sstream >> index;
map[index] = line.substr(pos+2);
}
int in;
while (std::cin >> in) {
std::map<int, std::string>::iterator i = map.find(in);
if (i == map.end())
std::cerr << "index not found" << std::endl;
else
std::cout << i->second << std::endl;
}
return 0;
}
Q1: How do you open text file in C++ ?
Q2: How do you read file line by line?
C++ Input/Output with files
Q3: How do you turn string with "," into array based on that delimiter?
How to split a string in C++?
This is probably not helpful, except for me; I get to practice using Spirit parsing (and karma output generation as a bonus):
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <fstream>
#include <map>
namespace qi = ::boost::spirit::qi;
namespace karma = ::boost::spirit::karma;
typedef std::map<int, std::string> index_t;
index_t read_index(const char* filename)
{
using boost::spirit::istream_iterator;
using namespace qi;
index_t result;
std::ifstream ifs(filename);
ifs.unsetf(std::ios::skipws);
istream_iterator begin(ifs), end;
if (!parse(begin, end, (int_ >> omit[lit(',') >> *char_(" \t")] >> *(char_ - eol)) % eol, result))
{
throw std::runtime_error("Unable to read/parse index file ");
}
return result; // http://en.wikipedia.org/wiki/Return_value_optimization
}
int main()
{
index_t index = read_index("input.txt");
using namespace karma;
std::cout << format(('[' << int_ << ": " << *char_ << ']') % eol, index) << std::endl;
for (int i=0; i<10; i++)
{
int lookup=rand()%6;
std::cout << "Random lookup: " << lookup << ": " << index[lookup] << std::endl;
}
return 0;
}
Oh, sample output:
[1: red]
[2: blue]
[3: green]
[4: orange]
Random lookup: 1: red
Random lookup: 4: orange
Random lookup: 3: green
Random lookup: 1: red
Random lookup: 5:
Random lookup: 1: red
Random lookup: 4: orange
Random lookup: 0:
Random lookup: 3: green
Random lookup: 1: red