here is my problem. i have some two dimensional data with changing dimensionality, that i want to read into an 2d-array of doubles. Furthermore, there are at some points not number in the file but "NaN"s, that i want to be replaced by a zero. I made my code working so far, but i only managed to read integers. Maybe you could help me out to read it as doubles?
Here is what i got so far:
void READER(char filepath [], int target [129][128])
{
//---------------------------- header double & int
int rowA = 0;
int colA = 0;
std::string line;
std::string x;
std::cout << "reading file: " << filepath << "\n";
std::cout << std::endl;
std::ifstream fileIN;
fileIN.open(filepath);
if (!fileIN.good())
std::cerr << "READING ERROR IN FILE: " << filepath << std::endl;
while (fileIN.good())
{
while (getline(fileIN, line))
{
std::istringstream streamA(line);
colA = 0;
while (streamA >> x)
{
boost::algorithm::replace_all(x, "NaN", "0");
boost::algorithm::replace_all(x, ",", ""); //. rein
// std::cout << string_to_int(x) << std::endl;
target [rowA][colA] = string_to_int(x);
colA++;
}
rowA++;
if(rowA%5 ==0)
{
std::cout << "*";
}
}
}
std::cout << " done." <<std::endl;
}
this writes the files into 'target'. The function string to int looks the following:
int string_to_int (const std::string& s)
{
std::istringstream i(s);
int x;
if(!(i >> x))
return 0;
return x;
}
here you find some example data:
"exactly, thats what i thought about doing with the line boost::algorithm::replace_all(x, ",", ""); by replacing , by ."
Use following function to convert to any type, say double :-
template <typename T>
T StringToNumber ( const std::string &Text )
{
std::istringstream ss(Text);
T result;
return ss >> result ? result : 0;
}
Call using :
boost::algorithm::replace_all(x, ",", "."); // Change , to .
std::cout << StringToNumber<double>(x) << std::endl;
Or
you can simply use boost::lexical_cast
std::cout<<boost::lexical_cast<double>( x )<<std::endl;
Make sure you have a double 2D array
Related
Im trying to get a float value from a file.txt into a string. When I output that value with std::stof(str) it gets rounded. Example, in the text file there's "101471.71", whet i use the std::stof(str) it returns "101472", how to I avoid this?
Here's a part of that code (some parts are in spanish, sorry :p):
double CaptureLine(std::string filepath, int fileline, int linesize)
{
std::fstream file;
std::string n_str, num_n;
int current_line = 0, n_size, filesize = FileSize(filepath);
char ch_n;
double n_float = 0.0;
int n_line = filesize - fileline;
file.open("registros.txt");
if (file.is_open()) {
while (!file.eof()) {
current_line++;
std::getline(file, n_str);
if (current_line == n_line) break;
}
if (current_line < n_line) {
std::cout << "Error" << std::endl;
return 1;
}
file.close();
}
n_size = n_str.length();
for (int i = linesize; i < n_size; i++) {
ch_n = n_str.at(i);
num_n.push_back(ch_n);
}
std::cout << ">>" << num_n << "<<\n";
n_float = std::stof(num_n); //Here's the error
return n_float;
}
The issue probably isn't with std::stof, but is probably with the default precision of 6 in std::cout. You can use std::setprecision to increase that precision and capture all of your digits.
Here's a program that demonstrates:
#include <iostream>
#include <iomanip>
#include <string>
int main() {
std::cout << 101471.71f << "\n";
std::cout << std::stof("101471.71") << "\n";
std::cout << std::setprecision(8) << 101471.71f << "\n";
std::cout << std::stof("101471.71") << "\n";
return 0;
}
Outputs:
101472
101472
101471.71
101471.71
Be aware that std::setprecision sticks to the std::cout stream after it's called. Notice how the above example calls it exactly once but its effect sticks around.
I know that there's many questions here about reading lines from a text file and storing it in a c++ struct. However, those questions all contain text files that have precisely the information they needed, such as a text file with :
Tom 123
Jim 234
However, what if my text file had something like
// The range of 'horizontal' indices, inclusive
// E.g. if the range is 0-4, then the indices are 0, 1, 2, 3, 4
GridX_IdxRange=0-8
// The range of 'vertical' indices, inclusive
// E.g. if the range is 0-3, then the indices are 0, 1, 2, 3
GridY_IdxRange=0-9
How would I be able to get just the numbers 0-8 and 0-9 besides the equal sign in gridX and gridY to be stored in my struct?
What I tried:
struct config {
int gridRange[2];
int gridX;
int gridY;
int cityId;
int cloudCover;
int pressure;
std::string cityName;
};
config openFile(std::string filename, config &con) {
std::fstream inputFile(filename.c_str(), std::fstream::in);
if (inputFile.is_open()) {
std::string line;
for (int i = 0; i < 2; i++) {
while (std::getline(inputFile, line)) {
inputFile >> con.gridRange[i];
std::cout << con.gridRange[i]; //to show what is stored in the array
}
}
//std::cout << range << std::endl;
std::cout << std::endl;
return con;
}
else {
std::cout << "Unable to open file" << std::endl;
}
}
Ignore the other variables in the struct as I still do not need them yet. The output I got was
// The range of 'horizontal' indices, inclusive
0
I managed to solve this by adding in if (line[0] != '/' && !line.empty()) { inside the while loop and creating a new string variable called lineArray[5]. The function now looks like this
config openFile(std::string filename, config &con) {
std::fstream inputFile(filename.c_str(), std::fstream::in);
if (inputFile.is_open()) {
std::string line;
std::string lineArray[5];
int count = 0;
while (std::getline(inputFile, line)) {
if (line[0] != '/' && !line.empty()) {
lineArray[count] = line;
count++;
}
}
std::cout << "Printing out " << lineArray[0] << std::endl;
std::cout << std::endl;
return con;
}
else {
std::cout << "Unable to open file" << std::endl;
}
}
And the output I'll get is Printing out GridX_IdxRange=0-8.
I'm learnig C++. Here is my problem: I'm trying to read data from a text file and save it to a map<string, struct> and then have it print out all the keys from the map preferably in alphabetical order. The data has 2 strigns and a float. I can't get this to print even after having tried many different solutions.
Heres what I've got so far:
Here is my struct:
struct category
{
std::string tram_stop;
float dist;
};
using Tram = std::map<std::string, std::vector<category>>;
Here is where I try to save the data to the map.
void store(Tram& tram, std::vector<std::string>& tram_data)
{
if (tram.find (tram_data.at (0)) == tram.end ())
{
tram[tram_data.at (0)] = {};
}
else
{
tram.at (tram_data.at (0)).push_back (category {tram_data.at (1), std::stof(tram_data.at(2))});
}
}
And here is main().
int main()
{
Tram tram;
print_rasse();
// Ask input filename.
std::string filename;
std::cout << "Give a name for input file: ";
std::cin >> filename;
// Read input file.
std::ifstream file_in;
file_in.open (filename);
if (!file_in.is_open ())
{
std::cout << INVALID_FILE << std::endl;
return EXIT_FAILURE;
}
std::vector<std::string> tram_data;
if (file_in.is_open())
{
std::string line;
while( std::getline(file_in,line) )
{
std::stringstream ss(line);
std::string tram_line, tram_stop, distance;
std::getline(ss,tram_line,';'); //std::cout<< ""<<tram_line <<" ";
std::getline(ss,tram_stop,';'); //std::cout<<" "<<tram_stop<<" ";
std::getline(ss,distance); //std::cout<<" "<<distance<< " ";
if (tram_line != "" && tram_stop != "")
{
tram_data.push_back (tram_line);
tram_data.push_back (tram_stop);
tram_data.push_back (distance);
//std::cout << tram_line << " " << distance << std::endl;
}
else
{
std::cout << INVALID_FORMAT << std::endl;
return EXIT_FAILURE;
}
}
file_in.close ();
store(tram, tram_data);
}
This is the part I think doesn't work. Tried different iterators too.
if (upper_com == "LINES")
{
std::cout << "All tramlines in alphabetical order:" << std::endl;
for (auto& item : tram)
{
std::cout << item.first << std::endl;
}
}
Your implementation of store will create a vector for the first item added for a particular tram_data[0] value, but will not add anything to the vector. This results in losing that first item, and can result in no output because of the empty vectors.
That function can be simplified:
void store(Tram& tram, std::vector<std::string>& tram_data)
{
if (tram_data.size() < 3) throw std::out_of_range();
tram[tram_data[0]].emplace_back(tram_data[1], std::stof(tram_data[2]));
}
You don't need to use at with tram because you want to create the entry if it doesn't exist. at with tram_data will result in an exception being thrown if there are fewer than three elements in tram_data, so that check has been moved outside all the accesses to the vector.
I have a function for converting integers into std::string.
std::stringstream Tools::toStringConverter;
std::string Tools::tempString;
std::string Tools::intToString(int num) {
toStringConverter.str(std::string());
toStringConverter << num;
toStringConverter >> tempString;
return tempString;
}
When I try to use this method while using iterators, I get correct result
for(Button b : buttons) {
std::cout << Tools::intToString(b.id);
}
Also output is correct if I try this:
std::cout<< intToString(3);
But when I use simple for loop, I always get 0.
for(int i = 0; i < NetworkManager::MAX_PLAYERS; i++) {
std::cout << Tools::intToString(i) << " ";
}
std::string Tools::intToString(int num) {
std::stringstream toStringConverter;
toStringConverter << num;
return toStringConverter.str();
}
Don't use global variables.
so I am trying to figure out how to sort CSV files to help organize data that I need for an economics paper. The files are massive and there are a lot of them (about 587 mb of zipper files). The files are organized by columns in that all the variable names are in the first line and all the data for that variable is all below it. My goal is to be able to only take the columns that start with the an indicated string (ex input: "MC1", Get: MC10RT2,MC1WE02,...) and then save them into a separate file. Does anyone have any advice as to what the form that the code should take?
Just for fun a small program that should work for you. The thing you'll be intersted in is boost::split(columns, str, boost::is_any_of(","), boost::token_compress_off); that here create a vector of string from your csv-style string.
Very basic example, but your question was an excuse to play a bit with boost string algorithms, that I did know but never used...
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <set>
// Typedefs for eye candy
typedef std::vector<std::string> Fields;
typedef std::vector<Fields> Results;
typedef std::set<unsigned long> Columns;
// Split the CSV string to a vector of string
Fields split_to_fields(const std::string& str)
{
Fields columns;
boost::split(columns, str, boost::is_any_of(","),
boost::token_compress_off);
return columns;
}
// Read all the wanted columns
Results read_columns_of_csv(std::istream& stream, const Columns& wanted_columns)
{
std::string str;
Results results;
while (getline(stream, str))
{
Fields line{split_to_fields(str)};
Fields fields;
for (unsigned long wanted_column: wanted_columns)
{
if (line.size() < wanted_column)
{
std::cerr << "Line " << (results.size() + 1 )
<< " does not contain enough fields: "
<< line.size() << " < " << wanted_column
<< std::endl;
}
else
{
fields.push_back(line[wanted_column]);
}
}
results.push_back(fields);
}
return results;
}
// Read the ids of the columns you want to get
Columns read_wanted_columns(unsigned long max_id)
{
Columns wanted_columns;
unsigned long column;
do
{
std::cin >> column;
if ((column < max_id)
&& (column > 0))
{
wanted_columns.insert(column - 1);
}
}
while (column > 0);
return wanted_columns;
}
// Whole read process (header + columns)
Results read_csv(std::istream& stream)
{
std::string str;
if (!getline(stream, str))
{
std::cerr << "Empty file !" << std::endl;
return Results{};
}
// Get the column name
Fields columns{split_to_fields(str)};
// Output the column with id
unsigned long column_id = 1;
std::cout
<< "Select one of the column by entering its id (enter 0 to end): "
<< std::endl;
for (const std::string elem: columns)
{
std::cout << column_id++ << ": " << elem << std::endl;
};
// Read the choosen cols
return read_columns_of_csv(stream, read_wanted_columns(column_id));
}
int main(int argc, char* argv[])
{
// Manage errors for filename
if (argc < 2)
{
std::cerr << "Please specify a filename" << std::endl;
return -1;
}
std::ifstream file(argv[1]);
if (!file)
{
std::cerr << "Invalid filename: " << argv[1] << std::endl;
return -2;
}
// Process
Results results{read_csv(file)};
// Output
unsigned long line = 1;
std::cout << "Results: " << results.size() << " lines" << std::endl;
for (Fields fields: results)
{
std::cout << line++ << ": ";
std::copy(fields.begin(), fields.end(),
std::ostream_iterator<std::string>(std::cout, ","));
std::cout << std::endl;
}
return 0;
}
I suggest using a vector of structures.
The structure will allow each row to have a different type.
Your program would take on the following structure:
Read data into a the vector.
Extra necessary fields out of each structure in the vector and write
to new file.
Close all files.