I am trying to read this .csv file and here is an example of the data:
1.33286E+12 0 -20.790001 -4.49 -0.762739 -3.364226 -8.962189
1.33286E+12 0 -21.059999 -4.46 -0.721878 -3.255263 -8.989429
The problem is with the first column row 1 and 2. In the excel file it says the numbers in the cells are displayed as 1.33286E+12 and when you click on the cell it says they are 1332856031313 and 1332856031328 but the program is reading them as 1.33286E+12 but I need the whole number 1332856031313 and 1332856031328.
The code:
inputfile.open(word1.c_str());
while (getline (inputfile, line)) //while line reads good
{
istringstream linestream(line); //allows manipulation of string
string str;
while (getline (linestream, item, ',')) //extract character and store in item until ','
{
char * cstr, *p;
cstr = new char [item.size()+1];
strcpy(cstr, item.c_str());
p = strtok(cstr, " ");
while (p!=NULL) //while not at the end loop
{ // double e = atof(p); // coverts p to double
value++;
if( value == 1)
{ double e = atof(p); // coverts p to double
if(m ==1)
cout << time[0]<<"\n";
ostringstream str1;
str1 << e;
str = str1.str();
string str2;
str2.append(str.begin(), str.end());
const char * convert = str2.c_str();
e = atof(convert);
time[m] = e*0.001;
m++;
//if(m >=192542)
//cout << time[m-1]<<"\n";
}
p = strtok(NULL, " ");
}
delete[] cstr; //delete cstr to free up space.
}
count ++;
value = 0;
}
inputfile.close();
If the number 1332856031313 is being serialised as 1.33286E+12, there is no way to get it back in the deserialisation process. Information in the form of those 6 extra significant digits is gone forever. You need to make sure that when the CSV file is generated, it is saved at full precision. I don't know how you might do this with Excel.
Also, your use of atof and const char* isn't very C++-esque. Consider using code like
double a, b, c, d;
linestream >> a >> b >> c >> d;
instead.
Rook has beaten me to it, but I will make one suggestion. Use a loop for decoding, testing on the stringstream status. OK, two suggestions: break your code into functions. Putting it all in one big lump makes it harder to understand.
void DecodeLine(const std::string &sLine,
std::vector<double> &vResults)
{
std::istringstream istr(sLine);
double d = 0;
istr >> d;
while (!istr.fail())
{
vResults.push_back(d);
istr >> d;
}
}
Related
I have a file with data like this
10000 9.425 1.00216 -0.149976
20000 19.425 0.973893 -0.135456
30000 29.425 1.01707 -0.115423
40000 39.425 1.0181 -0.12074
.
.
.
to get the data what I am doing is to read the whole line and then separate the line by the spaces to get the data I need. The problem is that the file has 3000 lines so I tried to get the line in a for loop
std::vector<std::string> a;
std::ifstream datas("Data/thermo_stability.dat");
std::string str;
char d=' ';
for(int i=0; i<n; i++)
{
std::getline(datas, str);
tokenize(str,d,a);
x[i]=std::atof((a[1]).c_str());
y[i]=std::atof((a[3]).c_str());
std::cout << x[i] << "\t" << y[i] << std::endl;
}
I noticed that something was wrong so I added that cout and found out that it was always getting the same line. How can I fix this problem? Why is not getting the next line after getline is called? When I do it outside the loop it goes to the next line.
EDIT
here is the tokenized function
void tokenize(std::string &str, char delim, std::vector<std::string> &out)
{
size_t start;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
{
end = str.find(delim, start);
out.push_back(str.substr(start, end - start));
}
}
Have some issues with that code:
I don't see where n is set so how do you know it is correct. The proper way to read a line is to call getline() and then test it worked (it can be done in a single line).
while(std::getline(datas, str)) {
// Successfully read a line from the file
}
You don't need to manually convert string to integers or floats. The stream library will do that automatically.
std::istringstream lineStream(std::move(str));
str.clear();
int value1; // please use better names:
double value2;
double value3;
double value4;
lineStream >> value1 >> value2 >> value3 >> value4;
I am writing a program that reads data from a file.
the .txt file looks like this:
Text, numbers, : 5,3,5
Text, numbers, : 1,3,7,8
I was successful in extracting the "Text" and "Numbers", However when I came across the numbers after the : "5,3,5", I was really stuck.
I need to change these numbers to ints and add them to a vector of int, so I got rid of the commas, then tried using stoi to convert them to ints, however, stoi was only "extracting" the first number, in this case, only 5 and 1, this is what I've tried:
while(getline(file, line)){
stringstream ss(line);
getline(ss, text, ',');
getline (ss, nums, ':');
getline (ss, numList, ',' );
replace(numList.begin(), numList.end(), ',' , ' ');
vec.push_back(stoi(numList));
randomStruct str = {text, nums, numList};
randomStructVec.push_back(str);
}
I need the output to look like this when printing the elements of the vector:
5 3 5
1 3 7 8
what I'm getting is :
5
1
and other times I get duplicate numbers as well:
5
1111
or
5555
11
I need a way to make the stoi function convert all the numbers on that one line of string to ints, and store them in a vec of ints.
Any help, would be greatly appreciated.
Check out my solution at How do I tokenize a string that uses the String Toolkit Library
Here is a paired down version for your case:
#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp> //String Toolkit Library
const char *whitespace = " \t\r\n\f";
const char *whitespace_and_punctuation = " \t\r\n\f;,=";
int main()
{
// parsing a string into a vector of integers with separators
// of spaces and punctuation
std::string s("3; 4, 4, 8,-1");
std::vector<int> values;
if( strtk::parse( s, whitespace_and_punctuation, values ) )
{
for(size_t i = 0; i < values.size(); ++i )
std::cout << values[i] << std::endl;
}
return 0;
}
You will notice the conversion of the values into a vector of integers.
The library is header only. The library is VERY fast and can handle most anything you need to do with a string and parsing.
There is too main problem in your code.
First of all getline (ss, numList, ',' ); will stop on the first value of the list. In fact, when your list is 5,3,5, getline (ss, numList, ','); will read 5 then , so it will stop. At this point, numValue == "5"
This is quite simple to fix : Just remove the delimiter char, so getline(ss, numList);. Using this, numValue == "5,3,5"
Alright, now you have all your value. You replace ',' by ' ' in order to separate your numbers. Good, numList = "5 3 5".
And then is your second error : vec.push_back(stoi(numList));. stoi(numList) return an int and is not able to get through space characters. So it will only convert the first 5 and return it. You will never get the other numbers, as you don't even do a loop.
Here is my solution : convert your string to a stringstream and use >> operator
std::stringstream numStream(numList);
int value;
while(numList >> value)
vec.push_back(value);
So we end up with your final code (I removed stds, as it seems that you wrote using namespace std somewhere in your code)
struct randomStruct
{
string txt,
string nb,
vector<int> data
}
// -------
while(getline(file, line)){
stringstream ss(line);
getline(ss, text, ',');
getline (ss, nums, ':');
getline (ss, numList);
replace(numList.begin(), numList.end(), ',' , ' ');
stringstream numStream(numList);
int value;
while(numStream >> value)
vec.push_back(value);
randomStruct str = {text, nums, vec};
randomStructVec.push_back(str);
vec.clear();
}
// Accessing and printing data
for (auto str : randomStructVec)
{
for (auto i : str.data)
{
cout << i << " ";
}
cout << endl;
}
Use this function stoi_() in this function, I am converting a string into a number if that string contains the characters in range 0-9 otherwise create a new string and repeat this process until you reach the end of string. To handle negative numbers you have to add one more condition.
vector<int> stoi_(string s){
vector<int>ans;
int i = 0;
int n =s.length();
while(i<n){
string temp = ""; // current number
if(s[i]=='-' && (i+1<n && (s[i+1]>='0' && s[i+1]<='9'))){ // handle -ve numbers
temp = temp + s[i];
i++;
}
while(i<n && s[i]>='0' && s[i]<='9'){ // if current character is number append it into cur number
temp = temp + s[i];
i++;
}
if(temp.length()>0){
ans.push_back(stoi(temp)); // here using stoi() for simplicity
}
else{
i++;
}
}
return ans;
}
I am trying to read two numbers of double values stored on same line separated by comma from a text file or csv file into a vector. The numbers are stored in this format like this [1688.37, 115.14]. I would like to read this numbers from the file and then store in array so can access the first and second number. My code compiles but will not display the numbers. Here the code in C++.
ifstream file("C:/arrow1.txt",ios::app);
double s;
std::vector<double> data;
while(file>>s){
data.push_back(s);
}
for(int i=0; i<data.size(); i++){
std::cout<<data[i]<<std::endl;
}
This code reads and displays the numbers but in same line as string. Then I don't know how to access the first and second number
ifstream fh("C:/arrow1.csv",ios::app);
std::vector<std::string> vs;
std::string s;
while(fh>>s){
vs.push_back(s);
}
for(int i=0; i<vs.size(); i++){
std::cout<<vs[i]<<std::endl;
}
Any help?
You could use your second attempt in combination with atof()(Have a look here). It converts a string into double.
Copy the first number into a second string and then use atof() to get your double value.
Example:
// Your copied values from the file
std::string s1 = "[1688.37,";
std::string s2 = "115.14]";
// Copy the number without "[" and ",", the length of the number is variable
std::string str1 = s1.substr(1, s1.length()-1); // Copy the first number in str1
double firstNum = atof(str1.c_str());
cout << firstNum << endl;
// Copy the second number without "]"
std::string str2 = s2.substr(0, s2.length()-1); // Copy the second number in str2
double secondNum = atof(str2.c_str());
cout << secondNum << endl;
If the istream (be it stringstream, ifstream) declared as s in the following example contains a well-defined pattern of any number of lines of the format [float1, float2] - you can use the >> operator (stream extraction operator in this case) on the stream to read the values and the get call to read the ,, [, ] and newline characters, like in the following example:
std::vector<double> fpvec;
char c;
double in;
while( s.eof() == false)
{
s.get(c);
s >> in;
fpvec.push_back(in);
s.get(c);
s >> in;
fpvec.push_back(in);
s.get(c);
s.get(c);
// Print last two inserted elements - testing purposes only
unsigned int size = fpvec.size();
std::cout << fpvec[size-2] <<" and "<< fpvec[size-1] << std::endl;
}
I am quite new in c++ and programming so sorry in advance in my question repeats. I have a text file of 3 lines:
7
00000000000000000000000*0000
0 0 0 R 0
What I need to do is read 2nd line and write it into an array as char. But I must not include 3rd line because it will go to a completely different matrix. My code so far :
ifstream input;
input.open("input1.txt");
input >> start;
char a=0;
string line;
while (getline(input, line))
{
a=0;
istringstream iss(line);
int length = line.size();
for (int i=0; i<length; i++)
{
iss >> a;
A[i] = a;
cout << A[i] << " " << i << endl;
}
}
input.close();
However, with this code it always starts new array for 3rd line. What am I doing wrong? What is the easiest way to fix it? Thank you.
-----------------------------Update--------------------------------------
I have modified the code but it still does not work properly. I am getting this kind of result : 5)-└ instead of correct one. My current code:
void Read(int &numLines, int &start, vector<char>&A, char B[][5])
{
ifstream input;
input.open("input.txt");
input >> start;
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
string line;
if(getline(input, line))
{
for(char temp: line)
{
A.push_back(temp);
}
}
input.close();
}
A here is a vector I want to write 2nd line to, char by char
Start is just an integer in which I am storing 1st line (7)
Thank you very much for advices
Mixing >> and std::getline is non-trivial. For example, after input >> start; the end of line marker is left in the stream in case it's still needed. In your case it isn't, and it is picked off by the subsequent call to getline, resulting in a read of an empty line.
This is what's complicating your read of line and forcing the while loop and test for empty lines.
Step through your program with your development environment's debugger and you'll see what I'm talking about. Get used to using the debugger. It's possibly the best programming productivity tool you'll ever encounter.
The easiest way to fix it is to place
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
after
input >> start;
to eat up the end of the line (and anything else that might be on that line. This needs the addition of #include<limits> to get std::numeric_limits<std::streamsize>::max.
Then you can remove the while loop and replace it with
if (getline(input, line))
No loop, not chance of consuming multiple lines from the file. And the logic for reading and processing the third line can follow.
Side note: instead of that for loop, consider
int i = 0;
while (iss >> a)
{
A[i] = a;
cout << A[i] << " " << i << endl;
i++;
}
This will march through iss until it hits the end of the line. You can also throw iss out entirely and just read the characters directly out of line.
int i = 0;
for(char temp: line)
{
A[i] = temp;
}
And A should probably be a vector if it isn't already to reduce the chances of buffer overruns.
for(char temp: line)
{
A.push_back(temp);
}
I would go with something like this:
std::string start;
std::string Astring;
ifstream input;
input.open("input.txt");
input >> start;
input >> Astring;
// If you really want a char array
char * A = new char[Astring.size()];
for (unsigned int i = 0; i < Astring.size(); i++) {
A[i] = Astring[i];
}
// Don't forget to delete after use
delete[] A;
Moreover, if you just need the char array as an input to something else later, you can call Astring.c_str() instead of that for loop, which returns a C-style char array.
I have a text file that I am inputting data in from, but I can't seem to get it right.
Here are two lines from the text file as an example (these aren't real people don't worry):
Michael Davidson 153 Summer Avenue Evanston CO 80303
Ingrid Johnson 2075 Woodland Road Aurora IL 60507
Here is the code I have to load the text file and put the data into a struct. I am still new to C++(obviously) and I'm having a hard time using get and >> together. The code I have below, works fine until I get to the "state" and then something goes wrong. Thanks for the help!
//constants
const int FIRST_NAME_LEN = 11;
const int LAST_NAME_LEN = 13;
const int ADDRESS = 25;
const int CITY_NAME_LEN = 16;
const int STATE_LEN = 3;
//define struct data types
struct CustomerType {
char firstName[FIRST_NAME_LEN];
char lastName[LAST_NAME_LEN];
char streetAddress[ADDRESS];
char city[CITY_NAME_LEN];
char state[STATE_LEN];
int zipCode;
};
//prototype function
ifstream& getInfo(CustomerType& CT_Struct, ifstream& infile);
int main() {
//declare struct objects
CustomerType CT_Struct;
ifstream infile("PGM951_customers.txt");
if(!infile) {
cerr << "Could not open the input file." << endl;
exit(1); //terminates the program
}
//call the function
getInfo(CT_Struct, infile);
return 0;
}
ifstream& getInfo(CustomerType& CT_Struct, ifstream& infile) {
while(infile) {
infile.get(CT_Struct.firstName, sizeof(CT_Struct.firstName));
infile.get(CT_Struct.lastName, sizeof(CT_Struct.lastName));
infile.get(CT_Struct.streetAddress, sizeof(CT_Struct.streetAddress));
infile.get(CT_Struct.city, sizeof(CT_Struct.city));
infile.get(CT_Struct.state, sizeof(CT_Struct.state));
infile >> ws;
infile >> CT_Struct.zipCode;
cout << CT_Struct.firstName << " | " << CT_Struct.lastName << " | " << CT_Struct.streetAddress
<< " | " << CT_Struct.city << " | " << CT_Struct.state << " | " << CT_Struct.zipCode << endl;
}
return infile;
}
=== edit ===========
Reading in the state at 8 char was just me messing around and then I forgot to change it back...sorry.
The problem is istream::get() breaks for streetAddress which has spaces in it.
One way is to tokenize the input line first into say, a vector of strings and then depending on the number of tokens convert these to appropriate fields of your CustomerType:
vector<string> tokenize(string& line, char delim=' ') {
vector<string> tokens;
size_t spos = 0, epos = string::npos;
while ((epos = line.find_first_of(delim)) != string::npos) {
tokens.push_back(line.substr(spos, epos - spos));
spos = epos;
}
return tokens;
}
I'd rather a stream extraction operator for CustomerType :
struct CustomerType {
friend istream& operator>>(istream& i, CustomerType& c);
string firstName, lastName, ...;
// ...
};
istream& operator>>(istream& i, CustomerType& c) {
i >> c.firstName >> c.lastName;
string s1, s2, s3;
i >> s1 >> s2 >> s3;
c.streetAddress = s1 + s2 + s3;
i >> c.city >> c.state >> c.zipCode;
return i;
}
You're getting 8 characters for State, which includes all your zipcode, and is larger than your field.
It'd also be tempting to use the skipws operator:
infile >> skipws >> CT_Struct.firstName
>> CT_Struct.lastName
>> ... ;
(Update: that's what I get for doing that from memory. This is more closely approximating correct.)
If I were you I would start again from scratch. I would:
use std::strings instead of character arrays for your data
reads line at a time from the file using std::getline
parse the line up using a stringstream
avoid mixing formatted and unformatted input
My approach to this would be the following:
1) Read each line into a null terminated buffer.
2) Use a split() function that you're gonna have to write. This function should take a string as its input and return a list. It should also take a separator. The separator in this case is ' '.
3) iterate over the list carefully (are there never middle names?) What about 1 word, or 3 word street names? Since many of these columns are really variable in number of words, and you have no seperator other than whitspace, this may prove a fairly tough task. If you NEVER have middle names, you could assume the first two columns are first and last name. You know for sure what the last two are. Everything between them could be assigned to a single address field.