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.
Related
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 am reading a text file which contains integers separated by a new line. Like this.
5006179359870233335
13649319959095080120
17557656355642819359
15239379993672357891
3900144417965865322
12715826487550005702
From this file, I want to access each integer in a loop and compare it with another, in order to match those two. In function File_read() I can print the integers. But what I want is to get it integer by integer outside the function. For example in main method, if there is a integer called x, I want to check whether x equals one of the integers in my text file.
string File_read() {
std::ifstream my_file("H:\\Sanduni_projects\\testing\\test.txt",
std::ifstream::binary);
if (my_file) {
string line;
for (int i = 0; i < 25; i++){
getline(my_file, line);
//cout << line << endl;
return line;
}
if (my_file)
std::cout << "all characters read successfully."<<endl;
my_file.close();
}
return 0;
}
Never return unconditionally inside a loop.
You are returning unconditionally from inside the loop. This causes the caller to exit the loop and return from the function during the first iteration.
for (int i = 0; i < 25; i++){
getline(my_file, line);
return line; // <-- Return from function (rest of iterations unreachable). Bad.
}
No need to reinvent stuff
Use the standard library to read the numbers, e.g., into a container std::vector.
std::vector<unsigned long long> v{std::istream_iterator<unsigned long long>{my_file},
std::istream_iterator<unsigned long long>{}};
Notice the value type of unsigned long long that is needed to fit the large numbers (you're pushing ~64 bits here).
Find a match
Use, e.g., std::find to find a possible match among the parsed numbers.
auto key = 15239379993672357891ull;
if (auto it = std::find(std::begin(v), std::end(v), key); it != std::end(v)) {
std::cout << "Key found at line " << std::distance(std::begin(v), it) + 1 << std::endl;
}
Here, I'm using a C++1z if(init; condition) statement to limit the scope of the iterator it to inside the if statement. It's optional of course.
Live example
You are, currently, just returning the first number (as a std::string and not a number). If you remove the return statement in your loop you can, of course, print each of them. Here is a slightly modified version of your File_read function that will return a std::vector<unsigned long long> that contains all the numbers. Then you can use this vector in, e.g., your main function to do your processing.
std::vector<unsigned long long> File_read()
{
std::vector<unsigned long long> numbers;
std::ifstream my_file("H:\\Sanduni_projects\\testing\\test.txt"); // Text files are not 'binany', i.e., removed std::ifstream::binary
if (my_file)
{
std::string line;
for (int i = 0; i < 25; i++)
{
std::getline(my_file, line);
numbers.push_back(std::stoull(line));
}
if (my_file)
{
std::cout << "all characters read successfully." << std::endl;
}
// my_file.close(); // Do not do this manually
}
return numbers;
}
Usage example:
int main()
{
unsigned long long x = /* some number */;
// Read all the numbers
std::vector<unsigned long long> vl = File_read();
// Run through all the numbers
for (unsigned long long y : vl)
{
// Check if any of the numbers are equal to x
if (x == y)
{
// There is a match...
// Do stuff
}
}
}
Update
The numbers cannot be held by in a long, however unsigned long long is sufficient.
std::vector<long> File_read(){
vector<long> numbers;
ifstream my_file("H:\\Sanduni_projects\\testing\\test.txt",
std::ifstream::binary);
if (my_file) {
string line;
for (int i = 0; i < frames_sec; i++){
getline(my_file, line);
numbers.push_back(std::stol(line));
}
if (my_file)
std::cout << "all characters read successfully." << endl;
else
std::cout << "error: only " << my_file.gcount() << " could be read" << endl;
my_file.close();
}
else{
cout << "File can not be opened" << endl;
}
return numbers;
}
Although the someone gives the answers that works correctly, I want to share my code.
#include <memory>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
#define MAX_SIZE 4096
class FileRead
{
public:
FileRead(string path) :_file(path)
{
Reset();
}
void Reset()
{
memset(_buff, 0, MAX_SIZE);
}
string ReadLine()
{
if (!_file.is_open())
{
cout << "error open file" << endl;
return "";
}
if (!_file.eof())
{
Reset();
_file.getline(_buff,MAX_SIZE);
return string(_buff);
}
else
{
cout << "read file finished." << endl;
return "";
}
}
private:
ifstream _file;
string _line;
char _buff[MAX_SIZE];
};
int _tmain(int argc, _TCHAR* argv[])
{
FileRead fr("H:\\Sanduni_projects\\testing\\test.txt");
string line;
while (!(line = fr.ReadLine()).empty())
{
//do some compare..
}
return 0;
}
The other answers are correct about how return works, but there is something that acts how you thought return acted.
using string_coro = boost::coroutines::asymmetric_coroutine<std::string>
void File_read(string_coro::push_type & yield) {
std::ifstream my_file("H:\\Sanduni_projects\\testing\\test.txt", std::ifstream::binary);
if (my_file) {
string line;
for (int i = 0; i < 25; i++){
getline(my_file, line);
yield (line);
}
if (my_file)
std::cout << "all characters read successfully." << std::endl;
my_file.close();
}
}
Which is used like this
string_coro::pull_type(File_read) strings;
for (const std::string & s : strings)
std::cout << s << endl;
I am almost done with my project, but I still have one small thing that needs to be done...I need to run the entire program for each file in the directory. There are about 200 files in total. Below is the main class of the program that needs to run. I'm thinking I will put the entire thing in a do-while loop and run it until there are no more .dat files in the directory, but I'm not sure if that will work. Obviously, I'd like to replace the hard-coded file names with variables...I'm just not sure how to do that, either. Please let me know if you need clarification. I've been working on this project for a while and I'm getting kind of brain-numb. Thanks in advance for your help!
Edit My test directory is on a Windows machine, but it will be uploaded to a linux machine at school.
int main() {
NearestNeighbor face;
//string path = "C:\Users\Documents\NetBeansProjects\CSCE350";
//string searchPattern = "*dat";
// string fullSearchPath = path + searchPattern;
/*TEMPLATE DATA*/
/***********************************************************************************/
fstream templateData;
double data = 0.0;
templateData.open("003_template.dat", std::ios::in);
//check that the file is opened
if (!templateData.is_open()) {
std::cerr << "Template: Nooooooo!\n";
exit(0);
}
/*************************************************************************************/
//fill the templateVector with the values from templateData
std::vector<std::vector<double> > templateVector;
std::string line;
while (getline(templateData, line, '\n'))
templateVector.push_back(face.splitData(line));
//testing the contents of the templateVector
// cout << "TemplateVector: ";
// for (unsigned i = 0u; i != templateVector.size(); ++i) {
//
// std::cout << "Index[" << i << "] ";
// for(double value : templateVector[i])
// std::cout << value << " ";
// std::cout << "\n";
// }
/*QUERY DATA*/
/************************************************************************************/
std::ifstream inFile("003_AU01_query.dat", std::ios::in);
std::vector<double> queryVector;
double pixel = 0.0;
// Check that the file opened
if (!inFile.is_open()) {
std::cerr << "Query: Nooooooo!\n";
exit(1);
}
// fill the queryVector with the query data
while (inFile >> pixel) {
queryVector.push_back(pixel);
}
inFile.close();
// testing the content of the query vector
// for (unsigned i =0u; i < pixels.size(); i++){
// std::cout << "Index["<< i << "] " << pixels[i];
// }
// std::cout << "\n";
/*OUTPUT SCALAR PRODUCT*/
/****************************************************************************************/
vector<double> theList;
/*break out each of the vectors from the templateVector and compute the scalar product*/
for (auto& vec : templateVector) {
int i;
cout << "\nscalar_product: Index[" << i << "] " << face.scalar_product(vec, queryVector);
theList.push_back(face.scalar_product(vec, queryVector));//fill theList vector with the computations
i++;
std::cout << "\n";
}
//make sure that the sorted products are output with their original index numbers
vector<pair<int, double> > sorted;
sorted.reserve(theList.size());
for(size_t i = 0.00; i != theList.size(); i++){
sorted.push_back(make_pair(theList[i], i));
}
//sort the scalar products and print out the 10 closest neighbors
face.quickSort(sorted);
cout << "\nVector after sort:\n";
for(size_t i = 0; i < 10; i++){
cout << "idx: " << sorted[i].second << " " << "val: " << sorted[i].first << endl;
}
}
A solution in bash:
#!/bin/bash
for file in `ls`
do
./program $file
done
Of course you'd have to modify your main function to take an argument to pass to the fstream constructor:
int main(int argc, char **argv)
{
if (argc != 2)
{
// some error handling code
}
ifstream templateData(argv[1]);
if (!templateData)
{
// more error handling
}
// process the file
}
From your code it is windows.
This code will print all the *.dat file names in your folder:
Instead of printing do whatever you like.
first You'll need to include:
#include <windows.h>
Now to the code:
const wstring dir = L"C:\\Users\\Documents\\NetBeansProjects\\CSCE350";
const wstring ext = L"dat";
wstring findstr = dir;
findstr += L"\\*.";
findstr += ext;
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFile(findstr.c_str(),&ffd);
do{
if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
wstring path = dir;
path += L"\\";
path += ffd.cFileName;
wcout<< path<<endl;
}
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
EDIT:
On linux you can give your program a parameter path/to/dir/*.dat and you'll get the parameters trough argv, maybe its the better solution.
But if you insist to do it with code it is like this:
includes:
#include <sys/types.h>
#include <dirent.h>
Now the code:
const string dirname = "path/to/dir";
const string ext = ".dat";
DIR *dir;
struct dirent *de;
if((dir = opendir(dirname.c_str())) == NULL) {
//error... check errno and so on
cerr<<"Error..."<<endl;
}else{
while ((de = readdir(dir)) != NULL) {
//you can use stat to check if is is file or dir...
string filename(de->d_name);
if(ext = filename.substr(filename.size()-ext.size())){
cout<<dirname<<"/"<<filename<<endl;
}
}
closedir(dp);
}
Good luck
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
I need help loading a custom file format into my program made in c++...
I know there's a simple way of doing this but I think I'm using the
wrong terms to search for it online...
My custom format for 3d objects is as follows:
NumVerts 6
//verts (float)
-1
-1
0
1
-1
0
-1
1
0
1
-1
0
1
1
0
-1
1
0
//texture (float)
0
0
1
0
0
1
1
0
1
1
0
1
//index (int)
0
1
2
1
3
2
And that is a quad... (yeas; I know... horrible format... but it's what I'm using for an android game).
I want to make a function in c++ for my editor (SDL + OpenGL for windows) that loads these files into data... Unfortunately though I know how to export this format with C++, I can't figure out how to import them... I wish to use the fstream commands...
If someone could quickly write out a simple version I'd be really thankful.
I just it to do the following:
Find text file from input string
read "NumVerts" and grab the integer written after it
loop through the next (NumVerts*3) lines and grab each number as a float
loop though the next (NumVerts*2) lines and grab each number as a float
loop through the next (NumVerts*1) lines and grab each number as an Int
(skip any line that starts with "//")
close file.
Thank you for reading and any help would be really good right now... or a relivent link that is quite simple and reads strings from files and grabs numbers from them to be placed into memory...
I really just want to finish this game and it's getting really stressful trying to locate helpful tutorials.
Update: updated the script... I accidently forgot to seperate 1's and 0's...
Hope this helps. Incidentally, you have the wrong number of vertex components - you need 18 of them.
#include <algorithm>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
int load_3d_object(const std::string& filename,
std::vector<float>& vertComponents,
std::vector<float>& texComponents,
std::vector<int>& indices)
{
std::ifstream fs(filename.c_str());
std::string line;
if(!std::getline(fs, line))
{
throw std::runtime_error("The input file is empty");
}
if(line.substr(0,8) != "NumVerts")
{
throw std::runtime_error("The first line must start with NumVerts");
}
// Extract the number of vertices.
int numVerts = lexical_cast<int>(line.substr(line.find(' ') + 1));
// Read in the vertex components, texture components and indices.
while(std::getline(fs, line))
{
boost::trim(line);
if(line.substr(0,2) == "//") continue;
if((int)vertComponents.size() < numVerts * 3) vertComponents.push_back(lexical_cast<float>(line));
else if((int)texComponents.size() < numVerts * 2) texComponents.push_back(lexical_cast<float>(line));
else indices.push_back(lexical_cast<int>(line));
}
return numVerts;
}
int main()
{
std::vector<float> vertComponents;
std::vector<float> texComponents;
std::vector<int> indices;
try
{
int numVerts = load_3d_object("input.txt", vertComponents, texComponents, indices);
}
catch(std::exception& e)
{
std::cout << e.what() << '\n';
}
return 0;
}
Hopefully this might help (minimal error checking):
int _get_num_verts_value(std::ifstream& a_in)
{
char buf[128];
int result = -1;
a_in.getline(buf, sizeof(buf));
while (a_in.good())
{
if (a_in.gcount() > 9)
{
string s(buf);
if (0 == s.find("NumVerts "))
{
result = atoi(s.substr(8).c_str());
break;
}
}
a_in.getline(buf, sizeof(buf));
}
return result;
}
template <typename T>
void _get_values(std::ifstream& a_in, std::vector<T>& a_values)
{
char buf[128];
a_in.getline(buf, sizeof(buf));
int i = 0;
while (a_in.good())
{
string line(buf);
if (0 != line.find("//"))
{
a_values[i++] = boost::lexical_cast<T>(line);
// All read ?
if (i == a_values.capacity())
{
break;
}
}
a_in.getline(buf, sizeof(buf));
}
}
int main(int /*a_argc*/, char** /*a_argv*/)
{
ifstream in("test.txt");
const int num_verts_value = _get_num_verts_value(in);
std::vector<float> verts(num_verts_value * 3);
_get_values<float>(in, verts);
std::vector<float> textures(num_verts_value * 2);
_get_values<float>(in, textures);
std::vector<int> indexes(num_verts_value);
_get_values<int>(in, indexes);
in.close();
return 0;
}
using a flat file for this purpose could be a bad idea. it's pretty fragile. I prefer to a binary file or a self-described file (e.g xml).
std::ifstream in("in");
char buf[256];
std::string line;
int NumVerts;
in >> buf >> NumVerts;
std::vector<float> verts(NumVerts * 3);
std::getline(in, line);//read the rest of the line
std::getline(in, line);//skip the comment line
std::for_each(verts.begin(), verts.end(), [&in](float& par){
in >> par;
});
std::vector<float> texture(NumVerts * 2);
std::getline(in, line);//read the rest of the line
std::getline(in, line);//skip the comment line
std::for_each(texture.begin(), texture.end(), [&in](float& par){
in >> par;
});
std::vector<int> index(NumVerts);
std::getline(in, line);//read the rest of the line
std::getline(in, line);//skip the comment line
std::for_each(index.begin(), index.end(), [&in](int& par){
in >> par;
});
For the links on how to DIY:
Tutorial: http://www.cplusplus.com/doc/tutorial/files/
Reference: http://new.cplusplus.com/reference/iostream/fstream/
Example code:
#include <string>
#include <iostream> // needed for printing stuff out
#include <fstream> // Needed to read the file
#include <stdexcept>
#include <vector>
using namespace std;
struct Vertice {
double x,y,z; // 3 part stuff to read
double a,b; // 2 part stuff to read
int i; // 1 int thing to read
};
/// Reads a number ignoring comment lines (and any other line that isn't a number)
template <typename T>
T readNumber(istream& data) {
char lastChar;
while (data.good()) {
data >> lastChar; // Don't use peek as that won't skip whitespace
data.putback(lastChar);
if (( (lastChar >= '0') && (lastChar <= '9') ) || (lastChar == '-')) {
// If we're looking at a number read and return it
T result;
data >> result;
return result;
} else {
// If it's not part of a number .. assume it's a comment line and skip the whole line
string commentLine;
getline(data, commentLine);
// TODO: Maybe just skip '//' lines and throw an exception for everything else..
}
}
throw exception("Couldn't read file");
}
double readDouble(istream& data) { return readNumber<double>(data); }
int readInt(istream& data) { return readNumber<int>(data); }
int main(int argc, char** argv) {
if (argc != 2)
cout << "Usage: " << argv[0] << " [DATA_FILE_NAME]" << endl;
else {
fstream data(argv[1], ios_base::in);
data >> skipws; // Skip whitespace
string lastString;
long numVerts = -1;
// Read in words ignoring everything until we hit a 'NumVerts'
while (numVerts < 0) {
data >> lastString;
if (lastString == "NumVerts")
data >> numVerts;
}
// We know how many to get now
typedef vector<Vertice> Verts;
Verts verts(numVerts);
// Read in the triples
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
i->x = readDouble(data);
i->y = readDouble(data);
i->z = readDouble(data);
}
// Read in the pairs (some other data)
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
i->a = readDouble(data);
i->b = readDouble(data);
}
// Read in the single integer value
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
i->i = readInt(data);
}
// Print out all we found
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
cout << "Vertice" << endl
<< " x: " << i->x << endl
<< " y: " << i->y << endl
<< " z: " << i->z << endl
<< " a: " << i->a << endl
<< " b: " << i->b << endl
<< " i: " << i->i << endl
<< endl;
}
}
}
The example code throws an error on the provided files as there isn't enough data to fill out the 'NumVerts'.