I need to traverse a file in a vertical manner. If suppose the file contents are:
adg
beh
cfi
It should print the file as:
abc
def
ghi
The length for each line will be same(i.e. all lines will be of length 3 for above example). I have written a code but it doesn't traverse the file as required.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main()
{
fstream fs;
fs.open("asd.txt",ios::in);
string str;
char *ch = new char();
int lineLen = 0, k = 0;
if(getline(fs,str))
{
lineLen = str.length();
}
fs.seekg(0);
if(lineLen > 0)
{
for(int i = 0;i<lineLen;i++)
{
fs.seekg(i+k*lineLen);
while(fs.read(ch,1))
{
k++;
fs.seekg(i+k*lineLen);
cout<<*ch;
}
k = 0;
}
}
fs.close();
cin.ignore();
}
I am a bit new to file handling and couldn't find the mistake. Also, is there a better approach for this to be followed?
Pretty much your way with some little tweaks
//lines = no. of lines in file
fs.seekg(0, fs.beg);
fs.clear();
if(lineLen > 0)
{
for(int k = 0; k < lineLen; k++) {
for(int i = 0;i<lines;i++){
fs.seekg(k+i * (lineLen + 2), fs.beg); //use lines + 2
if(fs.read (ch,1));
cout << *ch;
}
cout << endl;
}
Untested pseudo-code that may give you some ideas. Basically, load the whole file into a 2d vector of characters for easy access. It will use more memory than reading directly from the file but this won't matter unless the file is very big.
vector<vector<char>> filemap;
string line;
while (getline(filestream, line))
{
filemap.push_back(vector<char>(line.begin(), line.end()));
}
for (int x = 0; x < XSIZE; x++)
{
for (int y = 0; y < YSIZE; y++)
{
filestream << filemap[y][x]; // note x/y are opposite way round in 2d vectors
}
filestream << '\n';
}
You might find this task much simpler if you were to use mmap(2). There may be a C++ equivalent or wrapper, but I'm afraid I'm not much of an expert on that front. Hopefully someone will come along with a better answer if that's the case.
Here's a quick C (not ++) example. I'll see if I can google around and C++ify it some more:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
int fd = open("input", O_RDONLY);
struct stat s;
fstat(fd, &s);
// map the file as one big string
char *c = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
// calculate sizes
int columns = strchr(c, '\n') - c; // first newline delimits a row
int stride = columns + 1; // count the newline!
int rows = s.st_size / stride; // all rows are the same length
for (int x = 0; x < columns; x++)
{
for (int y = 0; y < rows; y++)
{
putchar(c[y*stride + x]);
}
putchar('\n');
}
munmap(c, s.st_size);
close(fd);
return 0;
}
Edit: A quick search around didn't turn up a much better way to handle this in C++ as far as I could tell. I mean, I can add a typecast on the mmap line and change the putchar calls to std::cout, but that doesn't really seem like it makes any difference.
Instead of trying to seek() repeatedly in the source file it is much easier and faster to simply read in the whole source file then generate output from the in-memory contents.
This sounds an awful like like a class assignment, so I won't simply write the answer for you. However this should point you in the right way -- Some PseodoCode is included
To avoid pain, it should presumably be safe to assume some upper bound on line length and max lines, i.e.,
const int MaxLines = 100;
const int MaxLength = 80;
int lineno, linelength;
// array of char pointers for each line
char *lines[] = (*lines[])malloc(Maxlines * sizeof(char*));
// ReadLoop
lineno = 0;
while (not eof)
{
getline(buffer);
if (++lineno++ == 1)
{
linelength = strlen(buffer);
}
else
{
if (linelength != strlen(buffer))
{
cout "Line # " << lineno << " does not match the expected length";
exit();
}
}
lines[lineno] = malloc(strlen(buffer)+1));
strcpy(lines[lineno], buffer);
}
int cc, linecnt = lineno;
// now all data in memory, output "vertical data"
for (cc = 0; cc < linelength; ++cc)
{
for (lineno=0; lineno<<linelength; ++lineno)
{
cout << lines[xx][yy]; // xx && yy left you to figure out
}
cout "\n";
}
Provided that your file is not enormous, there's no reason not to just slurp the whole thing into memory. There may be a more idiomatic way to do this in C++, but the following works:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
int main(int argc, char *argv[])
{
std::fstream infile("foo.txt");
std::vector<std::string> lines;
std::string line;
while(std::getline(infile,line)) {
lines.push_back(line);
}
int m=lines.size();
int n=lines[0].length();
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
std::cout << lines[j].at(i);
}
std::cout << std::endl;
}
return 0;
}
Problems arise when all the lines in the file are not the same length, of course.
And now, a version that “doesn't use any extra memory” (of course, it does, but not much):
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
int main(int argc, char *argv[])
{
std::fstream infile("foo.txt");
std::vector<std::string> lines;
std::string line;
std::getline(infile, line);
int n = line.length();
int m = 1+std::count(std::istreambuf_iterator<char>(infile),
std::istreambuf_iterator<char>(), '\n');
infile.clear();
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
infile.seekg(j*m+i);
std::cout << char(infile.peek());
}
std::cout << std::endl;
}
return 0;
}
Related
For a project I am currently working on, I have to read from a file and depending on the certain character in the file, output either a 1 or 0 to an array.
So here is an example of file input:
* * *
* * *
** ** **
*** *
And here is the function I have written to handle this:
void input (int cellGrid[][MAX]) //takes info from a .txt and puts it into an array
{
ifstream infile; //declare a file variable
int row;
int column;
int number;
infile.open("life.txt"); //open a file
while(infile>>row>>column) { //inserts bacteria places into array
cout << row << " " << column << endl;
cellGrid[row][column]=1; //makes it equal one if bacteria is present
}
infile.close(); //closes file
}
My thinking was that the function needs to see if there is a character that exists and if so, place a 1 in its respective position ([row][column]) in the array. However with this current code, nothing is input into my array.
Generally in C++ use std::vector wherever possible.
Array example:
You have to go through the file and record position of each *. Then set it to 1 for that position. Something like the following (we use getline and i as counter for rows, then we loop through the line using j as counter for columns):
#include <fstream>
#include <string>
using namespace std;
void input(int cellGrid[][100]) {
ifstream infile;
infile.open("life.txt");
int i = 0;
for (string line; getline(infile, line); ++i)
{
for (size_t j = 0; j < line.size(); ++j) {
if (line[j] == '*') {
cellGrid[i][j] = 1;
}
}
}
infile.close();
}
Vector example #1:
Here you can find a solution using std::vector. It will be always in a size rows x columns. One thing it requires is to pass default constructed vector and not constructed with vector(size_type count); c-tor. You can write your own version that doesn't have this problem:
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void input(vector<vector<int>> &cellGrid) {
ifstream infile;
infile.open("life.txt");
int i = 0;
for (string line; getline(infile, line); ++i)
{
cellGrid.push_back(vector<int>(line.size()));
for (size_t j = 0; j < line.size(); ++j) {
if (line[j] == '*') {
cellGrid[i][j] = 1;
}
}
}
infile.close();
}
int main() {
vector<vector<int>> cellGrid;
vector<vector<int>> cellGrid2(100);
input(cellGrid);
//input(cellGrid2); - THIS WILL THROW AN EXCEPTION
return 0;
}
Vector example #2:
It would be even better for you function to return a newly created and populated vector:
#include <fstream>
#include <string>
#include <vector>
using namespace std;
vector<vector<int>> input() {
ifstream infile;
infile.open("life.txt");
vector<vector<int>> cell_grid;
int i = 0;
for (string line; getline(infile, line); ++i)
{
cell_grid.push_back(vector<int>(line.size()));
for (size_t j = 0; j < line.size(); ++j) {
if (line[j] == '*') {
cell_grid[i][j] = 1;
}
}
}
infile.close();
return cell_grid;
}
int main() {
auto vec = input();
return 0;
}
My thinking goes like this:
set row to 0
while can read a line from file
set column to 0
for each character on line
if character is '*'
set cellGrid(row,column) to 1
else
set cellGrid(row,column) to 0
increment column
increment row
You may want additional logic to trap row or column trying to go out of bounds or characters that aren't ' ' or '*'.
I'm converting large graph files using a simple C++ program. Basically I mainly want to switch the delimiters used in the file. These graph files contain extremely long lines. My converting works fine for small files, but I lose edges in the output file for the large files. So I checked what the part of my code that reads actually returns and it seems the error is already in there. Using the following, I do not receive the full lines from the graph in curLineMetis or ssMetis (My guess is that there's an overflow somewhere and I lose the last part of the line thats too big for the allocated memory)
using namespace std;
void exampleFunc(const char* inputfileMetis) {
ifstream metisFile;
metisFile.open(inputfileMetis, ios::in);
if(!metisFile.is_open()) {
cout << "Could not open input file";
return;
}
long nMet = 0;
long mMet = 0;
string curLineMetis;
// skip first line
getline(metisFile, curLineMetis);
while(getline(metisFile, curLineMetis)) {
nMet++;
long targetMetis;
stringstream ssMetis(curLineMetis);
while(ssMetis >> targetMetis) {
mMet++;
}
}
}
I checked on google and it said std::string managed its own memory and would not overflow? I also tried it without the stringstream, just using an iterator over chars from the string before and the same happened. I'm using c++11 if that makes a difference. (the error expresses itself e.g. in mMet being different from the number of edges stated in the graph file itself, while the number of vertices nMet is the same, and the number of edges is also correct for smaller graphs)
I am not quite sure what is going wrong for you with your code. The standard library functions and types should be able to handle long lines. However, to reduce memory usage and to work around your problem you might want to process a part of a line at a time. Since the file format seemed interesting I wrote this program to read the graph into a vector<vector<int>>. It reproduces the graph file from that representation and produces a file identical to the input. Perhaps something in here will be of use to you. Good luck.
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
int
main(int argc, char* argv[])
{
size_t const buf_size = 40; // big enough for a 64bit integer and then some
char buf[buf_size];
assert(argc >= 2);
std::ifstream is (argv[1]);
assert(is);
std::vector<std::vector<int>> graph;
std::vector<int> line;
std::string header;
std::getline(is, header);
{ std::istringstream is_header (header);
size_t n;
is_header >> n;
graph.reserve(n);
}
char delim;
for (;;) {
is.get(buf, buf_size, ' ');
if (!is) break;
is.get(delim);
line.push_back(std::atoi(buf));
while (is.peek() == '\n') {
graph.emplace_back(std::move(line));
(void) is.get();
}
}
std::cout << graph.size() << '\n';
size_t max_row = graph[0].size();
size_t row_sum = graph[0].size();
for (size_t i = 1, I = graph.size(); i < I; ++i) {
max_row = std::max(max_row, graph[i].size());
row_sum += graph[i].size();
}
std::cout << max_row << '\n';
std::cout << row_sum << '\n';
std::ofstream os ("dup.graph");
os << header << '\n';
for (size_t i = 0, I = graph.size(); i < I; ++i) {
for (size_t j = 0, J = graph[i].size(); j < J; ++j)
os << graph[i][j] << ' ';
os << '\n';
}
return 0;
}
So I need to read a grid from a file,the grid's width and lengths is always the same.The problem is when I try to cout it on the last line it only shows about a half of it.
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
ifstream TestIn("test");
int main()
{
char Grid[1000][1000],s[1000];
int LungimeX,LungimeY,i,j;
TestIn.getline(s,1000);
//finding the length
LungimeX=strlen(s);
cout<<LungimeX<<endl;
//finding the width
while (!TestIn.eof()) {
TestIn.getline(s,1000);
LungimeY++;
}
cout<<LungimeY;
//reset .eof
TestIn.clear();
TestIn.seekg(0, TestIn.beg);
//get the grid into the array
for(i=1;i<=LungimeY;i++) {
for(j=1;j<=LungimeX;j++) {
TestIn.get(Grid[i][j]);
}}
for(i=1;i<=LungimeY;i++){
for(j=1;j<=LungimeX;j++){
cout<<Grid[i][j];
}}
return 0;
}
So yeah,any ideas how to fix this?
LungimeY isn't initialized
Need to read (to skip) the header line after the file rewind
Need to skip CR and/or LF characters after each line read when filling the array
You were not ignoring the newline character LungimeX is the length of the line not including the new line character. a simple solution could be when reading the file if newline character is encountered read the next character.
#include
#include
#include
using namespace std;
ifstream TestIn("test");
int main()
{
char Grid[1000][1000],s[1000];
int LungimeX,LungimeY,i,j;
TestIn.getline(s,1000);
//finding the length
LungimeX=strlen(s);
cout<<LungimeX<<endl;
//finding the width
while (!TestIn.eof()) {
TestIn.getline(s,1000);
LungimeY++;}
cout<<LungimeY;
//reset .eof
TestIn.clear();
TestIn.seekg (0, TestIn.beg);
//get the grid into the array
for(i=1;i<=LungimeY;i++){
for(j=1;j<=LungimeX;j++){
TestIn.get(Grid[i][j]);
if(Grid[i][j] == '\n') //check new line character
TestIn.get(Grid[i][j]);
}}
for(i=1;i<=LungimeY;i++){
for(j=1;j<=LungimeX;j++){
cout<<Grid[i][j];
}
cout<<endl;
}
return 0;
}
And yes Please use 0 indexing in C++ you are wasting memory this way.
What about this kind of more C++ approach ?
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::string fname("test.txt");
std::ifstream f(fname.c_str());
std::vector<std::string> lines;
std::string line;
while(std::getline(f, line))
lines.push_back(line);
unsigned long int num_rows = lines.size();
unsigned long int num_cols = 0;
if(num_rows > 0)
num_cols = lines[0].length();
std::cout << "num_rows = " << num_rows << std::endl;
std::cout << "num_cols = " << num_cols << std::endl;
for(unsigned long int i = 0; i < num_rows; ++i)
{
for(unsigned long int j = 0; j < num_cols; ++j)
std::cout << lines[i][j];
std::cout << std::endl;
}
return 0;
}
I am a high school student programming as a hobby. I make free stuff and I am working on a game using opengl. I need to save and load data but when met with difficulty I made the following to test my methods.
The save file 'shiptest' is correct but when I open the second file 'shipout' which is created with the save data from 'shiptest' only the first line is there. At first I thought that my array wasn't loading any new data and the clear function wasn't getting rid of the first elements. I corrected this assumption by overwriting those lines after saving the data and observing that the saved lines were loaded after all. My new assumption is that the getline func is only getting the first line each time it's called; but i do not know how to fix this.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
unsigned short int shipPart;
float editShip[256][3];//part ID, x relative, y relative, r,g,b
float activeShip[256][3];
void CLEAR(bool edit)
{
for (int n = 0; n < 256; n++)
{
if (edit)
editShip[n][0] = -1;
else
activeShip[n][0] = -1;
}
}
void saveEdit(std::string name)
{
std::ofstream out;
out.open ("ship" + name + ".txt", std::ofstream::out);
for (int n = 0; n < 256; n++)
{
for (int i = 0; i < 3; i++)
{
if (editShip[n][0] == -1)
break;
out << editShip[n][i] << " ";
}
out << "\n";
}
out.close();
}
void load(std::string name, bool edit)
{
CLEAR(edit);
std::ifstream in;
in.open ("ship" + name + ".txt", std::ifstream::in);
std::string line, buf;
std::stringstream ss;
int i;
for (int n = 0; n < 3; n++)
{
getline(in, line);
ss << line;
i=0;
while (ss >> buf)
{
if (edit)
editShip[n][i] = atof(buf.c_str());
else
activeShip[n][i] = atof(buf.c_str());
i++;
}
}
in.close();
}
int main()
{
for (int n = 0; n < 256; n++)
{
editShip[n][0] = -1;
activeShip[n][0] = -1;
}
editShip[0][0] = 5;
editShip[0][1] = .11;
editShip[0][2] = .22;
editShip[1][0] = 4;
editShip[1][1] = .33;
editShip[1][2] = .44;
editShip[2][0] = 3;
editShip[2][1] = .55;
editShip[2][2] = .66;
saveEdit("test");
editShip[0][0] = 5000;
editShip[0][1] = 8978;
editShip[0][2] = 8888;
load("test",1);
saveEdit("out");
std::cout << "Hello world!" << std::endl;
return 0;
}
In load(), you keep appending more lines to your stringstream ss but its eof flag is probably remaining set from the previous time through the loop, so even though there's more to read from it, eof is already set so it won't continue providing data via operator>>(). If you simply call ss.clear() at the top of the for() loop, you'll start with an empty stringstream on each loop, and I think you'll get what you want.
In your load() function:
for (int n = 0; n < 3; n++)
{
ss.clear(); //< Clear ss here before you use it!
getline(in, line);
ss << line;
i=0;
while (ss >> buf)
{
if (edit)
editShip[n][i] = atof(buf.c_str());
else
activeShip[n][i] = atof(buf.c_str());
i++;
}
}
Getline() was working just fine. Just clear the stringstream before you use it and you're good to go. Ran this code on my computer and it works as desired.
EDIT: Ack! Just saw that phonetagger said the same thing while I was making my answer. He deserves the +1's not me.
This code is supposed to make a array of strings, randomly order them, and then print the order. Unfortunately it adds a blank line in one of the spaces ( i think this is getline's doing). Any ideas how to fix that? I tried setting array [0] = NULL; it complains about operators...
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <string>
#include <cstdlib>
using std::cout;
using std::endl;
using namespace std;
void swap (string &one, string &two)
{
string tmp = one;
one = two;
two = tmp;
}
int rand_loc (int size)
{
return (rand() % size);
}
int main()
{
srand(time(NULL));
int size;
cin >> size;
string *array = new string[size];
//array[0] = NULL ;
for (int x = 0; x < size; x++)
{
getline(cin, array[x]);
}
//for (int x = 0; x < size; x++)
//{
// swap (array[rand_loc(size)], array[rand_loc(size)]);
//}
cout << endl;
for (int x = 0; x < size; x++)
{
//out << array[x] << endl;
int y = x + 1;
cout<<y<<"."<<" "<<array[x]<<endl;
}
delete[] array;
}
The first call to getline() will immediately hit the newline that the user entered after inputting size, and will therefore return an empty string. Try to call cin.ignore(255, '\n'); before the first call to getline(). This will skip up to 255 (an arbitrarily selected number) characters until a \n is encountered (and the newline will be skipped as well).
Edit: As #Johnsyweb and #ildjarn point out, std::numeric_limits<streamsize>::max() is a much better choice than 255.