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'.
Related
Solution: Use if (argc !=3) and getline(row_as_stringstream, substr, ';')
This is a continuation of my previous question: error: no matching function for call to, which allowed the code to compile without any errors
I am new to C++ and I am trying to debug this code from my supervisor.
The original code took a csv file as input, containing rows and columns of integers
and strings. Now we're reading in a txt file having the shape:
TEXT
0; INT; INT; INT; INT; ...
0; INT; INT; INT; INT; ...
18 more lines of the above numbers and semicolons
In that file I replaced in one instance the semicolons by line breaks and in another by empty spaces, because I was not sure which we needed. Solution: Use the txt file with the semicolons, but with the semicolons at the end of the lines removed.
It seems to have an issue with if (argc < 2 || argc > 2) . It throws the "Usage: ./a.o <> <>" error message. However, both are strict inequalities. Shouldn't this if be fine with 2 arguments? In the original code it read if (argc < 2 || argc > 3) , which I changed it back to. EDIT: As john pointed out the "program name is an argument" as well, so I want actually 3 not 2.
Both txt files (line breaks & spaces) seem to produce the same error message below
Error message: (For the int threshold I tried various values)
Registering only edges shorter than int.
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 1) >= this->size() (which is 1)
Aborted (core dumped)
Ideas:
I am aware what this error message means when we have a simple case, for example:
#include <vector>
int main()
{
std::vector<int> v;
v.push_back(123); // v has 1 element [0 to 0]
int x4 = v.at(3); // exception
}
We get an exception, because v has 1 element and element 3 does not exist.
However, in my code I am not sure what exactly to look for.
The original code read a csv with lines and columns, but in this case a matrix form with empty spaces in between is probably causing issues. Does that mean that I just want a txt file looking like a column vector? That is one file I tried, so it might be that the code is not happy with the amount of columns?
Relevant function:
int main(int argc, char** threshold_and_distanceMatrixfilename)
{
if (argc < 2 || argc > 3)
{
std::cerr << "Usage: ./distanceMatrixToSageGraph.o <threshold>
<distanceMatrix_file_calculated_fromDGEsingleCell_data>" << std::endl;
return -1;
}
string distanceMatrixfilename = threshold_and_distanceMatrixfilename[2];
int threshold = std::stoi(threshold_and_distanceMatrixfilename[1]);
std::ifstream distanceMatrixFile(distanceMatrixfilename);
if (!distanceMatrixFile)
{
std::cerr << "Error opening distanceMatrix file: " << distanceMatrixfilename << std::endl;
return -1;
}
string row;
std::getline(distanceMatrixFile, row); // discard the first row, which specifies the format of the file.
vector<vector<int>> the_entries;
while (std::getline(distanceMatrixFile, row))
{
std::stringstream row_as_stringstream(row);
int i; i = 0;
vector<string> row_as_vector;
while (row_as_stringstream.good())
{
string substr;
getline(row_as_stringstream, substr, ',');
row_as_vector.push_back( std::stoi(substr) );
};
the_entries.push_back(row_as_vector); //LINE 104
};
}
Whole code:
// Convert distanceMatrix tables of protein interactions to SAGE graph.
///////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <sstream>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <list>
#include <vector>
#include <tuple>
#include <algorithm>
using namespace std;
void writeGraphInSageFormat(string name, std::vector<std::vector<int>> TheEdges)
{
//////////////////////////////////////////////////////////////////////////////////////
// Write out the edges in SAGE format.
///////////////////////////////////////////////////////////////////////////////////////
int edgeNumber = TheEdges.size();
ofstream d1sageFile(name, ios::out);
d1sageFile << "g = Graph([" << endl;
for (int n = 0; n < edgeNumber; n++) {
d1sageFile << "(" << TheEdges[n][0] + 1 << "," << TheEdges[n][1] + 1 << ")," << endl;
}
d1sageFile << "])" << endl;
d1sageFile << "g.show()" << endl;
d1sageFile.close();
std::cout << "SAGE graph written into the file " << name << std::endl;
}
std::vector<std::vector<int>> ConvertEntriesMatrixToEdges(vector<vector<int>> the_entries, int threshold)
{
////////////////////////////////////////////////////////////////////////////////////////////
// Construct the edge-vertex incidence matrix (d_1) from the distanceMatrix entries matrix:
////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> proteinNames;
std::vector<std::vector<int>> TheEdges;
std::cout << "Registering only edges shorter than " << threshold << "." << std::endl;
int thisDistance;
for (int i = 0; i < the_entries.size(); i++)
{
for (int j = i + 1; j < the_entries.size(); j++)
{
// we could use the_entries.size() instead of the_entries.at(i).size(), because this is a square matrix.
thisDistance = the_entries.at(i).at(j);
if (thisDistance < threshold)
{
std::vector<int> CurrentEdge(2);
CurrentEdge[0] = i;
CurrentEdge[1] = j;
TheEdges.push_back(CurrentEdge);
};
};
};
return TheEdges;
}
///////////////////////////////////////////
// Main Program: Extract edges from a distanceMatrix file.
///////////////////////////////////////////
int main(int argc, char** threshold_and_distanceMatrixfilename)
{
if (argc < 2 || argc > 3)
{
std::cerr << "Usage: ./distanceMatrixToSageGraph.o <threshold> <distanceMatrix_file_calculated_fromDGEsingleCell_data>" << std::endl;
return -1;
}
string distanceMatrixfilename = threshold_and_distanceMatrixfilename[2];
int threshold = std::stoi(threshold_and_distanceMatrixfilename[1]);
std::ifstream distanceMatrixFile(distanceMatrixfilename);
if (!distanceMatrixFile) {
std::cerr << "Error opening distanceMatrix file: " << distanceMatrixfilename << std::endl;
return -1;
}
string row; //LINE 88
std::getline(distanceMatrixFile, row); // discard the first row, which specifies the format of the file.
vector<vector<int>> the_entries;
while (std::getline(distanceMatrixFile, row))
{
std::stringstream row_as_stringstream(row);
int i; i = 0;
vector<string> row_as_vector;
while (row_as_stringstream.good())
{
string substr;
getline(row_as_stringstream, substr, ',');
row_as_vector.push_back( std::stoi(substr) );
};
the_entries.push_back(row_as_vector); //LINE 104
};
////////////////////////////////////////////////////////////
// Now we assemble the entries to an edges matrix, and write it into a Sage file:
////////////////////////////////////////////////////////////
std::vector<std::vector<int>> TheEdges = ConvertEntriesMatrixToEdges(the_entries, threshold);
char outputFilename[60]; strcpy(outputFilename, distanceMatrixfilename.c_str()); strcat(outputFilename, "AtThreshold"); string thrshld = std::to_string(threshold); strcat(outputFilename, thrshld.c_str()); strcat(outputFilename, ".txt");
writeGraphInSageFormat(outputFilename, TheEdges);
return 0;
}
First question. What you're missing is that the program name is an argument, so
program arg1 arg2
is three arguments and argc will equal 3 not 2. You could have found this out for yourself by either using a debugger (you really need to learn how to use one, much better than asking here) or at the very least adding cout << "argc=" << argc << '\n'; to your code.
Second question, your original code was written for comma separated values, see the comma here getline(row_as_stringstream, substr, ','); so obviously you need to change it for semi colon or space separated values.
Above all though you need to learn to use a debugger. Trying to debug programs by looking at code is not easy.
I'm trying to read the following maze.txt file:
35
35
0
10
++++++++++S++++++++++++++++++++++++
++++++++++OOOOOOOOOOOOOOOOOOOOOOOOO
++++++++++O++++++++++++++++++O+++++
OOOOOOOOOOOOOOOOOOOOOOOOOO+++O++OOE
O+++++++++O++++++++++++++O+++O++O++
OOOOOO++++O++++++++++++++O+++O++O++
O++++O++++OOOOOOOOOOO++++O+OOO++O++
O++++O++++O+++++++++OOOO+O+++O++O++
OOO++OOOO+OOOOOO+++++++++++OOO++OOO
O+O+++++O++++++OOOOOOOOOO++O++++++O
O+OOOO++O++++++O++++++++O+++OOO+++O
O++++O++OOOOOOOO++++++++O+++O+O+++O
OOO++O++++++++++++++++++OOOOO+O+++O
++O++OOOOOOOOOOOOOOOO+++++++++OO++O
OOO+++++++++++++++++OOOOOO++++++++O
O++++++++++++++++++++++++O++OOOOOOO
+++++++++++++++++++++++++O++O++++++
OOOOOOOOOOOOOOOOOOOOOOOOOO++OOOOO++
O++++++++++++++++++++++++O++++++O++
OOOOOOO+++++++++++++++OOOOOOO+++O++
++++++++++++++++++++++O+++++OO++O++
OOOOOOOOOOOOOOOOOOOOOOO++++++O++O++
O++++++++++++++++++++++++++++O+OOOO
OOOO++++++++++++++++++++OOOOOO+O+++
+++OOOOOOOOO+++++++++++++++++++O+++
+++++O+++++OOOOOOOOOO++++++++OOO+++
+O+++OOOOO++++++O++++++++++++O+++++
+OOOOO+++O++++++OOOOOO+++++++O+++++
+++++++++++++++++++++OOOOOOOOO+++++
OOOOOOOOOOOOOOOOOOOOOO+++++++++++++
+++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++
The code works fine with the maze inside the code but I moved it out to a text file, which seems to be read but it is not working. It's giving me the error:
No matching function for call to 'mazeTravel'.
I'm not sure where to go from here. Any help would be appreciated!
#include <iostream>
#include <fstream>
using namespace std;
void printMaze(const char maze[][12], int xCoordinate, int yCoordinate);
int mazeTravel(char maze[][12], int xCoordinate, int yCoordinate, int direction);
int main()
{
char maze[35][35];
ifstream file;
file.open("maze.txt");
if (!file) {
cout << "Error reading file\n";
return -1;
}
else {
for (int row = 0; row < 35; row++) {
for (int column = 0; column < 35; column++) {
file >> maze[row][column];
int success = 0;
success = mazeTravel(maze, 2, 0, 1);
if (success == 1)
cout << "The maze has been solved.\n";
else
cout << "Sorry, the maze cannot be solved\n";
}
}
}
return 0;
}
You could use a std::vector of std::strings to represent your maze:
std::vector<std::string> maze;
To access its cells use
maze[row][column]; // with row being y and column x
To get the number of rows use
maze.size()
and
maze[0].size()
for the number of columns.
You could read such a maze like that (without error checking to not clutter the code):
std::vector<std::string> readMaze(std::istream &is)
{
std::size_t columns;
std::size_t rows;
is >> columns >> rows;
int foo; // sorry, don't know what the 3rd and 4th
is >> foo >> foo; // number is. a starting position, perhaps?
// ignore the rest of the current line:
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::string line;
std::vector<std::string> maze;
while (std::getline(is, line))
maze.push_back(line);
return maze;
}
An implementation (with error checking) could look like that:
#include <cstdlib> // EXIT_FAILURE
#include <limits> // std::numeric_limits<>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
// to not have to type std::vector<std::string> all over the place
using maze_type = std::vector<std::string>;
void printMazeCell(maze_type const &maze, std::size_t x, std::size_t y)
{
std::cout.put(maze[y][x]);
}
void printMaze(maze_type const &maze)
{
for (auto const &row : maze)
std::cout << row << '\n';
}
int mazeTravel(maze_type const &maze, std::size_t x, std::size_t y, int dir)
{
// access cells of the maze with maze[y][x]
// maze.size() for the number of columns and
// maze[0].size() for the number of rows
return 42;
}
maze_type readMaze(std::istream &is)
{
std::size_t columns;
if (!(is >> columns)) {
std::cerr << "Couldn't read the number of columns :(\n\n";
return maze_type{}; // return an empty maze on error
}
std::size_t rows;
if (!(is >> rows)) {
std::cerr << "Couldn't read the number of rows :(\n\n";
return maze_type{};
}
int foo;
is >> foo >> foo; // sorry, don't know what the 3rd and 4th number is
// ignore the rest of the current line:
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Trying to read a maze with " << columns << " columns ...\n";
std::string line;
maze_type maze;
while (std::getline(is, line)) {
if (line.length() != columns) {
std::cerr << "Found a row that contains only "
<< line.length() << " columns :(\n\n";
return maze_type{};
}
maze.push_back(line);
}
if (maze.size() != rows) {
std::cerr << "The maze only consists of "
<< maze.size() << " rows :(\n\n";
return maze_type{};
}
return maze;
}
int main()
{
char const *filename = "maze.txt";
std::ifstream is{ filename };
if (!is.is_open()) {
std::cerr << "Couldn't open \"" << filename << "\" for reading :(\n\n";
return EXIT_FAILURE;
}
maze_type maze = readMaze(is);
if (!maze.size()) { // readMaze returned an empty maze :(
std::cerr << "Bye.\n";
return EXIT_FAILURE;
}
printMaze(maze);
}
The problem that you don't have the implementation of
int mazeTravel(char maze[][12], int xCoordinate, int yCoordinate, int direction);
You should create the implementation like this:
int mazeTravel(char maze[][12], int xCoordinate, int yCoordinate, int direction)
{
// The implementation
return 0;
}
Another thing: You have to read the first some numbers at the beginning of the file.
35
35
0
10
After that you can read the matrix from the file
I've been working on an assignment to implement hashing. In it, I read through a text file called "proteins". The problem occurs when I try to copy it to another char array. Visual Studio throws a read access violation.
#include <iostream>
#include <fstream>
using namespace std;
struct arrayelement {
char protein[30];
int count;
};
arrayelement proteins[40];
int main()
{
char buffer[30];
// open source file
ifstream fin("proteins.txt");
if (!fin) { cerr << "Input file could not be opened\n"; exit(1); }
// loop through strings in file
while (fin >> buffer) {
int index = ((buffer[0] - 65) + (2 * (buffer[strlen(buffer)-1] - 65)) % 40);
while (true)
{
if (proteins[index].protein == buffer) // Found
{
proteins[index].count++;
break;
}
if (proteins[index].protein[0] == 0) // Empty
{
strcpy(proteins[index].protein, buffer); // <-- The error in question
proteins[index].count++;
break;
}
index++; // Collision
}
}
// close file
fin.close();
for (int i = 0; i <= 40; i++)
{
cout << proteins[i].protein << "\t" << proteins[i].count << "\n";
}
}
If you get more than 30 chars here:
while (fin >> buffer) {
... or if index >= 40 here:
strcpy(proteins[index].protein, buffer);
... the program will probably crash (Undefined behavior). Also, these char*'s will not be pointing at the same address, so the comparison will fail:
proteins[index].protein == buffer
What The Code Does:
The code below is supposed to format .cpp files for a printer, by limiting the number of chars in each line of code without losing any code. The code is supposed to go through the file char by char. It does so by opening an ifstream then using the get method to get each char till the EOF is found. As each char is grabbed it is pushed to a std::string (vector) as each char is pushed, a line char count is incremented. If a new line char is found before the line char count is larger than it's max size, it is reset and continues on the next line. If the line char count is 2 less than the max char count before a '\n' char is found a \, \n, ' ''s(x tab count) are appended to the end of the string being written to. Then the rest of the line is appended to the string. Though, if the last char added to the string is not a ' ' char then the string has all the chars between it's end and the closest to the left ' ' char removed and placed at the start of the new line, after the 5 ' ' chars on the new line. The last bit is where I think the error is.
Sample Line: (pretend is bigger than max char count)
LongLong123 longLineOfCode;\n;
Sample Output:
Bad:
LongLong123 longLineOfCo\\n
de;\n
Good:
LongLong123\\n
longLineOfCode;\n
The Error:
Unhandled exception at 0x55CC1949 (ucrtbased.dll) in SubmitFormater.exe: 0xC00000FD: Stack overflow (parameters:
0x00000000, 0x00F02000). occurred
Sample Command line:
110 5 "C:/Users/holycatcrusher/Documents/OC 2018 Fall/222/Labs/COSC_222_Data_Structures_Cpp/SubmitFormater.cpp"
The 110 is the number of chars that can be in a line, the 5 is the number of chars the tab is (5 blank spaces), and the long string is the address of the file to be formatted (make a copy of file as upon error the file entered will be destroyed). I am using the code that makes up this project as sample input (a copy of it).
The code:
/* SubmitFormater.cpp
This file was created by Bradley Honeyman
Oct 25, 2018
This file is used to format submit files for COSC 222 asignments
*/
#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);
int main(int argc, char **argv) {
std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//make sure there is the min number of commands
if (argc < 3) {
std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
return 0;
}
for (int i = 3; i < argc; i++) {
//open file and make sure it opens
std::ifstream input;
input.open(argv[i]);
if (!input.is_open()) {
std::cout << "Could not open the input file: " << argv[i] << std::endl;
return EXIT_FAILURE;
}
//process code
std::string *code = new std::string();
processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);
//check for error
if (code == NULL) {
return EXIT_FAILURE;
}
//output
input.close();
std::ofstream out(argv[i]);
if (!out.is_open()) {
std::cout << "could not write to output file" << std::endl;
return EXIT_FAILURE;
}
//print to file
std::cout << code->c_str() << std::endl;
out << code->c_str();
//close out delete code pointer
out.close();
delete code;
}
return 0;
}
/*
formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) {
//std::cout << "Processing" << std::endl;
//get char and check if is end of file
char current = input->get();
//std::cout << "\'" << current << "\'" << std::endl;
if (input->eof()) {
return code;
}
//incerment char count then check if are at furthest possible position
lineCharCount++;
//std::cout << lineCharCount << std::endl;
if (current == '\n') {
lineCharCount = 0;
//check if are at max char count, split code and put to new line
} else if (lineCharCount >= maxCharCount && current != '\\') {
//if not at the end of the line go back to the closest delimiter to break
std::string *pre = new std::string("");
bool fail = false;
if (current != '\n' && code->at(code->size() - 1)) {
code->push_back(current);
int i = code->size() - 1;
int back = 0;
for (i; i >= 0; i--) {
pre->push_back(code->at(i));
back++;
if (back > maxCharCount - tabSpaceSize - FORMAT_CHAR_COUNT) {
std::cout << "Can not format file because there isn't a place close enough to break at a delimiter!" << std::endl;
fail = true;
break;
}
}
//check for fail
if (!fail) {
//add delimiter if it needs to be
if (pre->size() > 0 && pre->at(pre->size() - 1) != delimiter) {
pre->push_back(delimiter);
}
//reverse prepending string, and remove code that is to be prepended
std::reverse(pre->begin(), pre->end());
code->assign(code->substr(0, code->size() - back - 1));
}
}
//insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
code->push_back('\\');
code->push_back('\n');
for (int i = 0; i < tabSpaceSize; i++) { code->push_back(' '); } //create tab
code->append(pre->c_str());
lineCharCount = tabSpaceSize + 1 + pre->size();
pre->clear();
delete pre;
}
//add char to code and run again
code->push_back(current);
return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);
}
/*
converts string to an int
*/
int toInt(char *input) {
int i = 0;
int out = 0;
while (input[i] != '\0') {
out = (out * 10) + (input[i] - '0');
i++;
}
return out;
}
Also, you can use the code above code as the sample .cpp file. Do remember to use a copy though, because the program will modify the file!
Call Stack Part 1 and 2(click images to be able to read them)
Note:
The cause of the problem is covered in the conversation.
After the amount of talking that went on this post I think It is worth posting what ended up being the solution for me. Also a few notes on what I concluded.
Code:
/* SubmitFormater.cpp
This file was created by Bradley Honeyman
Oct 25, 2018
This file is used to format submit files for COSC 222 asignments
*/
#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);
int main(int argc, char **argv) {
std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//make sure there is the min number of commands
if (argc < 3) {
std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
return 0;
}
for (int i = 3; i < argc; i++) {
//open file and make sure it opens
std::ifstream input;
input.open(argv[i]);
if (!input.is_open()) {
std::cout << "Could not open the input file: " << argv[i] << std::endl;
return EXIT_FAILURE;
}
//process code
std::string *code = new std::string();
processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);
//check for error
if (code == NULL) {
return EXIT_FAILURE;
}
//output
input.close();
std::ofstream out(argv[i]);
if (!out.is_open()) {
std::cout << "could not write to output file" << std::endl;
return EXIT_FAILURE;
}
//print to file
out << code->c_str();
//close out delete code pointer
out.close();
delete code;
}
return 0;
}
/*
formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) {
//get char and check if is end of file
char current = input->get();
if (input->eof()) {
return code;
}
//incerment char count then check if are at furthest possible position
lineCharCount++;
//std::cout << lineCharCount << std::endl;
if (current == '\n') {
lineCharCount = 0;
//check if are at max char count, split code and put to new line
} else if (lineCharCount >= maxCharCount && current != '\\') {
//if not at delimiter push everything to the right of the nearest delimiter to the left to pre
int back = 0;
std::string pre("");
if (current != delimiter) {
for (int i = code->size() - 1; i >= 0; i--) {
back++;
if (code->at(i) == delimiter) {
pre.push_back(code->at(i));
break;
} else {
pre.push_back(code->at(i));
}
}
//remove what was added to pre from code
std::reverse(pre.begin(), pre.end());
code->assign(code->substr(0, code->size() - back));
}
//insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
code->push_back('\\');
code->push_back('\n');
for (int i = 0; i < tabSpaceSize; i++) { code->push_back(' '); } //create tab
code->append(pre);
lineCharCount = tabSpaceSize + 1 + pre.size();
}
//add char to code and run again
code->push_back(current);
return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);
}
/*
converts string to an int
*/
int toInt(char *input) {
int i = 0;
int out = 0;
while (input[i] != '\0') {
out = (out * 10) + (input[i] - '0');
i++;
}
return out;
}
Notes:
The code only runs when compiled as a release version in VS
There was a logical error in the while loop
Compiler Optimization seems to solve the problem
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.