I have a CSV file with a bunch of columns, but I only need the information for the 11th column. How do I read through each line and skip to the 11th column in each line? I'm struggling to find clear information on how to read files in c++. This is what I have so far:
#include<iostream>
#include<fstream>
#include<vector>
#include <sstream>
#include <string>
std::string readStock(std::string fileName){
std::vector<std::string> ticker; //create vector
std::ifstream f(fileName, std::ios::in|std::ios:: binary|std::ios::ate);
std::string finalString = "";
if(f.is_open()){
std::string str;
std::getline(f,str); //skip the first row
while(std::getline(f,str)){ //read each line
std::istringstream s(str); //stringstream to parse csv
std::string val; //string to hold value
for(int i=1;i<=10;++i){ //skips everything until we get to the
column that we want
while(std::getline(s,val, ',')){
}
std::getline(s,val,',');
ticker.push_back(val);
}
f.close();
finalString = ticker.front();
}
}
else{
finalString="Could not open the file properly.";
}
return finalString;
}
int main(){
std::string st;
st=readStock("pr.csv");
std::cout<<st<<std::endl;
return 0;
}
There is a very simple solution for your problem.
You define a proxy class that reads one complete line, splits it into ALL tokens, using the dedicated functionality of the std::regex_token_iterator and then extracts the 11th element.
Using this proxy mechanism, you can use the std::istream_iterator to read the complete file, column 11, into a std::vector. For that we use the range constructor of the std::vector.
The result is a simple and short one-liner.
Please see:
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <regex>
#include <iterator>
#include <algorithm>
std::regex delimiter{ "," };
constexpr size_t targetColumn = 10U; // Target column is eleven
struct String11 { // Proxy for the input Iterator
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, String11& s11) {
// Read a complete line
if (std::string line{}; std::getline(is, line)) {
// Split it into tokens
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
// We only need one column
if (targetColumn < token.size()) {
// Get column 11
s11.result = token[targetColumn];
}
}
return is;
}
// Cast the type 'String11' to std::string
operator std::string() const { return result; }
// Temporary to hold the resulting string
std::string result{};
};
int main() {
// Open CSV fíle
if (std::ifstream csvFile{ "pr.csv" }; csvFile) {
// Read complete CSV file and get column 11 of each line
std::vector col11(std::istream_iterator<String11>(csvFile), {});
// Show output. Show all columns 11
std::copy(col11.begin(), col11.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
return 0;
}
EDIT:
For having output with doubles.
We just change one line in the cast operator in the proxy. That's all.
Even in main, there is no change in the read operatrion necessary. Through CTAD, the vector will be of type double.
Please see:
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <regex>
#include <iterator>
#include <algorithm>
std::regex delimiter{ "," };
constexpr size_t targetColumn = 10U; // Target column is eleven
struct String11 { // Proxy for the input Iterator
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, String11& s11) {
// Read a complete line
if (std::string line{}; std::getline(is, line)) {
// Split it into tokens
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
// We only need one column
if (targetColumn < token.size()) {
// Get column 11
s11.result = token[targetColumn];
}
}
return is;
}
// Cast the type 'String11' to double
operator double() const { return std::stod(result); }
// Temporary to hold the resulting string
std::string result{};
};
int main() {
// Open CSV fíle
if (std::ifstream csvFile{ "r:\\pr.csv" }; csvFile) {
// Read complete CSV file and get column 11 of each line
std::vector col11(std::istream_iterator<String11>(csvFile), {});
// Show output. Show all columns 11
std::copy(col11.begin(), col11.end(), std::ostream_iterator<double>(std::cout, "\n"));
}
return 0;
}
Output needs to adapted as well.
Related
I've been trying to read in characters from an external file to be put into a two-dimensional vector with type char. The elements must be able to be compared to certain values in order to navigate a maze given in "MazeSample.txt".
While I haven't been able to get characters be put into the vector, I was able to read and output the characters with the get and cout functions.
The following code is an attempt to read the vectors in the correct format, but provides an error in the end:
//MazeSample.txt
SWWOW
OOOOW
WWWOW
WEOOW
//source.cpp
vector<vector<char>> maze;
ifstream mazeFile;
char token;
mazeFile.open("MazeSample.txt");
while (!mazeFile.eof()) {
mazeFile.get(token); //reads a single character, goes to next char after loop
for (int row = 0; row < maze.size(); row++) {
for (int column = 0; column < maze.at(row).size(); row++) {
maze.push_back(token);
}
}
//cout << token;
}
mazeFile.close();
For the maze provided in "MazeSample.txt", I'd expect the maze vector to read each character row by row, mimicking the format of the maze sample.
In the above code, am provided with an error at maze.push_back(token):
"no instance of overloaded function "std::vector<_Ty, _Alloc>::push_back..." matches the argument list"
"argument types are: (char)"
"object type is: std::vector>, std::allocator>>>"
You are inserting char to vector<vector<char>>. You should create a vector<char>, insert the values of type char into that and then insert the vector<char> to vector<vector<char>> maze;. Here is the corrected version of your program. It can be written in simple ways but for your understanding, I have made made corrections on top of your program.
vector<vector<char>> maze;
ifstream mazeFile;
string token;
mazeFile.open("MazeSample.txt");
while (!mazeFile.eof()) {
std::getline(mazeFile, token); //reads an entire line
//Copy characters in entire row to vector of char
vector<char> vecRow;
vecRow.assign(token.begin(), token.end());
//Push entire row of characters in a vector
maze.push_back(vecRow);
}
mazeFile.close();
The reason for your problem is that you try to put a char into a std::vector of std vector. So you put a wrong type.
maze.at(row).push_back(token); would do it, but then no row exists. You also need to push_back and empty row, before you can write data to it.
That is your syntax error.
Then, your code could be drastically shortened by using C++ algorithms. See:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
std::istringstream testDataFile(
R"#(00000
11111
22222
33333
44444
)#");
// This is a proxy to read a complete line with the extractor operator
struct CompleteLineAsVectorOfChar {
// Overloaded Extractor Operator
friend std::istream& operator>>(std::istream& is, CompleteLineAsVectorOfChar& cl) {
std::string s{}; cl.completeLine.clear(); std::getline(is, s);
std::copy(s.begin(), s.end(), std::back_inserter(cl.completeLine));
return is; }
operator std::vector<char>() const { return completeLine; } // Type cast operator for expected value
std::vector<char> completeLine{};
};
int main()
{
// Read complete source file into maze, by simply defining the variable and using the range constructor
std::vector<std::vector<char>> maze { std::istream_iterator<CompleteLineAsVectorOfChar>(testDataFile), std::istream_iterator<CompleteLineAsVectorOfChar>() };
// Debug output: Copy all data to std::cout
std::for_each(maze.begin(), maze.end(), [](const std::vector<char> & l) {std::copy(l.begin(), l.end(), std::ostream_iterator<char>(std::cout, " ")); std::cout << '\n'; });
return 0;
}
But this is not the end. A std::vector<char>has no advantage over a string. You can work nearly have all the same functionality as a std::vector<char>. That is an improvement in design. The code would then look more like this:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
std::istringstream testDataFile(
R"#(00000
11111
22222
33333
44444
)#");
int main()
{
// Read complete source file into maze, by simply defining the variable and using the range constructor
std::vector<std::string> maze{ std::istream_iterator<std::string>(testDataFile), std::istream_iterator<std::string>() };
// Debug output: Copy all data to std::cout
std::copy(maze.begin(), maze.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
This is the by far more simpler solution. And it will serve your needs as well.
Please note: I used a istringstream for reading data, becuase I do not have a file on SO. But it is of cause the same as using any other stream (like an ifstream).
EDIT
The first solution read the source and put it directly into a std::vector<std::vector<char>>:
The 2nd solution put everything in the a std::vector<std::vector<std::string>> which is the most efficient solution. Also a std::string is nearly a std::vector<std::vector<char>>.
The OP requested a 3rd solution where we use the 2nd solution and then copy the std::vector<std::vector<std::string>> into a std::vector<std::vector<char>>.
Please see below
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
std::istringstream testDataFile(
R"#(00000
11111
22222
33333
44444
)#");
int main()
{
// Read complete source file into maze, by simply defining the variable and using the range constructor
std::vector<std::string> maze{ std::istream_iterator<std::string>(testDataFile), std::istream_iterator<std::string>() };
// Debug output: Copy all data to std::cout
std::copy(maze.begin(), maze.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
// Edit: Copy into a std::vector<std::vector<char> -------------------------------------------------------
std::cout << "\n\n\nSolution 3:\n\n";
// Define the new variable with number of lines from the first maze
std::vector<std::vector<char>> mazeChar(maze.size());
// Copy the data from the original maze
std::transform(
maze.begin(), // Source
maze.end(),
mazeChar.begin(), // Destination
[](const std::string & s) {
std::vector<char>vc; // Copy columns
std::copy(s.begin(), s.end(), std::back_inserter(vc));
return vc;
}
);
// Debug Output
std::for_each(
mazeChar.begin(),
mazeChar.end(),
[](const std::vector<char> & vc) {
std::copy(vc.begin(), vc.end(), std::ostream_iterator<char>(std::cout));
std::cout << '\n';
}
);
return 0;
}
Hope this helps . . .
Although I am not happy with the title of this question and this might be an odd question; bear with me, please.
So I have text files with content as follows:
& AAABBAB
this
& AAAAAAB
is
& BCAAAA
an
& BBBBBA
example
& BABABAB
text
where every other line starts with an identifier ('&'). Lines with said identifier should be lexicographically sorted, but I need it in a way such that the next line is dragged along to the new position in the output file with it.
This is what I am hoping to be the content of the output file.
& AAAAAAB
is
& AAABBAB
this
& BABABAB
text
& BBBBBA
example
& BCAAAA
an
With this, I can get the file content line-by-line:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
ifstream is("test.txt");
string str;
while(getline(is, str))
{
cout<<str<<endl;
}
return 0;
}
Is there an easy way to accomplish what I am looking for? Thanks for your help!
I'd bundle the pairs together while reading, making them easy to sort:
vector<pair<string, string>> vec; // first is identifier
vec.reserve(1000);
bool first = true;
while(getline(is, str))
{
if (first)
vec.emplace_back(str, string());
else
vec.back().second = str;
first = !first;
}
sort(vec.begin(), vec.end());
You can gather your lines by pairs into a vector of std::pair<std::string, std::string> :
using line_t = std::pair<std::string, std::string>;
std::vector<line_t> lines;
line_t pair_line;
while (std::getline(is, pair_line.first) &&
std::getline(is, pair_line.second)) {
lines.push_back(pair_line);
}
and sort them by their .first:
std::sort(begin(lines), end(lines),
[](auto const &l1, auto const &l2)
{ return l1.first < l2.first; });
DEMO
Yes, there is.
View the entire file as a map of key and value pairs, read into a std::map<std::string,std::string>, then output the map. Since string compares are lexicographic by default and maps have ordered keys, the map will do the sorting for you.
Here's a take that works nicely if you have a file that's too big to fit in memory, or, in general you need the efficiency.
It combines
a memory map¹
string views²
standard algorithms
Live On Coliru
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/utility/string_view.hpp>
#include <deque>
namespace io = boost::iostreams;
using boost::string_view;
auto map_entries(string_view input) {
std::deque<string_view> pairs;
while (!input.empty()) {
size_t pos = input.find('\n');
if (pos != string_view::npos)
pos = input.find('\n', pos + 1);
if (pos != string_view::npos)
pairs.push_back(input.substr(0, pos));
input.remove_prefix(pos + 1); // safe with unsigned wrap around
}
return pairs;
}
#include <iostream>
int main() {
io::mapped_file_source file("input.txt");
auto data = map_entries({ file.data(), file.size() });
std::stable_sort(data.begin(), data.end());
for (auto entry : data)
std::cout << entry << "\n";
}
Prints
& AAAAAAB
is
& AAABBAB
this
& BABABAB
text
& BBBBBA
example
& BCAAAA
an
¹ it's trivial to use POSIX mmap instead of the boost thing there
² you can use std::[experimental::]string_view if your compiler/library is recent enough
I'm looking to take a somewhat lengthy text file 50 rows by 2 columns, have a user input the file name and read it into a two demensional array. The text file is a combination of organized names (including commas) and numbers.
I can get the console to display the text file itself, but I'm stuck when it comes to orgazing the data into the array. I'm trying to devise a loop code involving getline and find in order for program through sort through the .txt, stop at a comma and record every character before that comma into a location (i.e [0] [0]) of the array. I'm aware that using vectors would be easier, but I'd like to solve this with an array.
Also, there is the issue of reading names (strings) into the array (int).
Please test this code:
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
#include <iterator>
template<typename Out>
void split(const std::string &s, char delim, Out result) {
std::stringstream ss;
ss.str(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;
}
int main()
{
std::ifstream file("test.txt", std::ios::binary);
std::string a, b;
int c;
std::vector<std::vector<std::string>> arr;
if (file)
{
while (file >> a )
{
std::vector<std::string> v = split(a, ',');
arr.push_back(v);
}
}
return 0;
}
my test.txt:
m,2
n,4
o,6
p,8
q,10
I'm making a function importcsv() which takes in a filename and outputs a 2D array. For some reason, whenever I use the following version of importcsv(), the compiler runs smoothly, but the executable always returns a "segmentation fault: 11" error.
typedef vector<vector<double> > matrix;
matrix importcsv(string filename)
{
ifstream myfile (filename); //Constructs a stream, and then asssociates the stream with the file "filename"
matrix contents; // Vector which will store the contents of the stream.
int i, j;
while(!myfile.eof())
{
if(myfile.get()==','){++j;}
else if(myfile.get()=='\n'){++i; j=0;}
else{
contents[i][j]=2;}
}
return contents;
}
Can anyone find the source of the error? btw I have the following header:
#include <fstream>
#include <iostream>
#include <array>
#include <vector>
using namespace std;
You are getting "segmentation fault: 11" since you have not allocated memory for contents.
contents[i][j] will work only if contents has something in it.
You can divide reading of the file and constructing the matrix into various parts:
Reading all the numbers in a line and treating it as a row of contents.
Reading a number from the line and treating it as a column of a row.
This way, the program can be simplified. This also helps you easily isolate problems when there are any and fix them.
typedef vector<vector<double> > matrix;
double readNextNumber(std::istream& str)
{
double n = 0.0;
str >> n;
// Skip until we get ',' or '\n'
while (str)
{
int c = str.getc();
if ( c == ',' || c == '\n' || c == EOF )
break;
}
return n;
}
std::vector<double> importRow(std::ifstram& myfile)
{
std::string line;
std::vector<double> row;
// Read a line as a string.
// Then parse the string using std::istringstream.
// When you have finished parsing the line, you know
// you have finished constructing a row of the matrix.
std::getline(myfile, line);
if ( myfile )
{
std::istringstream str(line);
while (str)
{
double n = readNextNumber(str);
if (str)
{
row.push_back(n);
}
}
}
return row;
}
matrix importcsv(string filename)
{
ifstream myfile (filename); //Constructs a stream, and then asssociates the stream with the file "filename"
matrix contents; // Vector which will store the contents of the stream.
while(!myfile.eof())
{
std::vector<double> row = importRow(myfile);
if (myfile)
{
contents.push_back(row);
}
}
return contents;
}
You haven't defined the size of contents. So by default, it will be a vector of 0 element. Therefore the calls to the operator[] will lead to a segmentatin fault.
Implementing the advice of the others here, the quick fix is to use resize() before reading each value into the array:
//WARNING: THIS PROGRAM UTILIZES C++11
#include <fstream>
#include <iostream>
#include <array>
#include <vector>
#include <cctype>
#include <thread>
using namespace std;
typedef vector<vector<double> > matrix;
matrix importcsv(string filename)
{
ifstream myfile ("wavelengthtorgb.csv"); //Constructs a stream, and then asssociates the stream with the file "filename".
matrix contents {{0.0}};
char nextchar; double data; int i,j;
while(!myfile.eof())
{
myfile.get(nextchar);
if(nextchar==',')
{
++j;
contents[i].resize(j+1);
cout<<"encountered a comma."<<" contents is now " <<i+1<<" x "<<j+1<<'\n';
}
else if(isspace(nextchar))
{
myfile.get(); //You might not need this line - first run with this line, and if there is an error, delete it, and try again.
++i;
contents.resize(i+1);
j=0;
contents[i].resize(j+1);
cout<<"encountered a carriage return."<<" contents is now " <<i+1<<" x "<<j+1<<'\n';
}
else
{
myfile.unget();
myfile >> data;
contents[i][j]=data;
cout<< "encountered a double."<<" contents("<<i<<','<<j<<")="<<data<<'\n';
}
}
return contents;
}
#include <fstream>
#include <vector>
#include <string>
struct values {
int a,b,c,d;
};
int main() {
std::ifstream myfile("myfile.txt");
std::vector<values> vect;
// Format of text file is:
// sdasjkdh234sldaksdj23asd125as24dfs
// asdac5234sdf435ljk...35wsd63jj
// asdad234sdf35gg567dfg554ddd
}
I need to create a vector from a file where each line contains four integers hidden between random characters, and put them into a vector of structs where each integer is mapped to the four fields in the struct values. The file has no whitespaces.
What is the easiest way to do this?
std::string line;
while(getline(cin, line))
{
auto s = std::remove_if(line.begin(), line.end(), [](auto& c) { return !isdigit(c); });
line.erase(s, line.end());
vect.push_back(std::stoi(line));
}