Logic for reading rows and columns from a text file (textparser) C++ - c++

I'm really stuck with this problem I'm having for reading rows and columns from a text file. We're using text files that our prof gave us. I have the functionality running so when the user in puts "numrows (file)" the number of rows in that file prints out.
However, every time I enter the text files, it's giving me 19 for both. The first text file only has 4 rows and the other one has 7. I know my logic is wrong, but I have no idea how to fix it.
Here's what I have for the numrows function:
int numrows(string line) {
ifstream ifs;
int i;
int row = 0;
int array [10] = {0};
while (ifs.good()) {
while (getline(ifs, line)) {
istringstream stream(line);
row = 0;
while(stream >>i) {
array[row] = i;
row++;
}
}
}
}
and here's the numcols:
int numcols(string line) {
int col = 0;
int i;
int arrayA[10] = {0};
ifstream ifs;
while (ifs.good()) {
istringstream streamA(line);
col = 0;
while (streamA >>i){
arrayA[col] = i;
col++;
}
}
}
edit: #chris yes, I wasn't sure what value to return as well. Here's my main:
int main() {
string fname, line;
ifstream ifs;
cout << "---- Enter a file name : ";
while (getline(cin, fname)) { // Ctrl-Z/D to quit!
// tries to open the file whose name is in string fname
ifs.open(fname.c_str());
if(fname.substr(0,8)=="numrows ") {
line.clear();
for (int i = 8; i<fname.length(); i++) {
line = line+fname[i];
}
cout << numrows (line) << endl;
ifs.close();
}
}
return 0;
}

This problem can be more easily solved by opening the text file as an ifstream, and then using std::get to process your input.
You can try for comparison against '\n' as the end of line character, and implement a pair of counters, one for columns on a line, the other for lines.
If you have variable length columns, you might want to store the values of (numColumns in a line) in a std::vector<int>, using myVector.push_back(numColumns) or similar.
Both links are to the cplusplus.com/reference section, which can provide a large amount of information about C++ and the STL.
Edited-in overview of possible workflow
You want one program, which will take a filename, and an 'operation', in this case "numrows" or "numcols". As such, your first steps are to find out the filename, and operation.
Your current implementation of this (in your question, after editing) won't work. Using cin should however be fine. Place this earlier in your main(), before opening a file.
Use substr like you have, or alternatively, search for a space character. Assume that the input after this is your filename, and the input in the first section is your operation. Store these values.
After this, try to open your file. If the file opens successfully, continue. If it won't open, then complain to the user for a bad input, and go back to the beginning, and ask again.
Once you have your file successfully open, check which type of calculation you want to run. Counting a number of rows is fairly easy - you can go through the file one character at a time, and count the number that are equal to '\n', the line-end character. Some files might use carriage-returns, line-feeds, etc - these have different characters, but are both a) unlikely to be what you have and b) easily looked up!
A number of columns is more complicated, because your rows might not all have the same number of columns. If your input is 1 25 21 abs 3k, do you want the value to be 5? If so, you can count the number of space characters on the line and add one. If instead, you want a value of 14 (each character and each space), then just count the characters based on the number of times you call get() before reaching a '\n' character. The use of a vector as explained below to store these values might be of interest.
Having calculated these two values (or value and set of values), you can output based on the value of your 'operation' variable. For example,
if (storedOperationName == "numcols") {
cout<< "The number of values in each column is " << numColsVal << endl;
}
If you have a vector of column values, you could output all of them, using
for (int pos = 0; pos < numColsVal.size(); pos++) {
cout<< numColsVal[pos] << " ";
}
Following all of this, you can return a value from your main() of 0, or you can just end the program (C++ now considers no return value from main to a be a return of 0), or you can ask for another filename, and repeat until some other method is used to end the program.
Further details
std::get() with no arguments will return the next character of an ifstream, using the example code format
std::ifstream myFileStream;
myFileStream.open("myFileName.txt");
nextCharacter = myFileStream.get(); // You should, before this, implement a loop.
// A possible loop condition might include something like `while myFileStream.good()`
// See the linked page on std::get()
if (nextCharacter == '\n')
{ // You have a line break here }
You could use this type of structure, along with a pair of counters as described earlier, to count the number of characters on a line, and the number of lines before the EOF (end of file).
If you want to store the number of characters on a line, for each line, you could use
std::vector<int> charPerLine;
int numberOfCharactersOnThisLine = 0;
while (...)
{
numberOfCharactersOnThisLine = 0
// Other parts of the loop here, including a numberOfCharactersOnThisLine++; statement
if (endOfLineCondition)
{
charPerLine.push_back(numberOfCharactersOnThisLine); // This stores the value in the vector
}
}
You should #include <vector> and either specific std:: before, or use a using namespace std; statement near the top. People will advise against using namespaces like this, but it can be convenient (which is also a good reason to avoid it, sort of!)

Related

Summing comma separated ints from a text file and then storing to an array in C++

I was tasked to read 3 rows of 5 comma separated values from a text file, sum up each column, and store the result in an array called bins. I am struggling to read the ints from the text file as they are comma separated. I first need to clarify how to read just the ints.
My next thought was to store the ints from the file into an array called "calc", and use the index of each element to sum up the values. I would then store these results into the "bins" array.
Here is some code I have tried to read the comma separated ints yet I cannot seem to get it to work.
int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;
int calc[15] = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o};
ifstream myfile;
myfile.open("values.txt");
for(int i = 0; i <= 15; i++)
{
myfile >> calc[i];
myfile.close();
a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];
cout << calc[i] << endl;
}
I am really new to working with code and I dont quite understand how to work with values in this manner. It is a simple task yet I cannot seem how to implement it with code.
I am really new to working with code and I dont[sic] quite understand how to work with values in this manner.
OK, I have several tips for you:
① separate your tasks
You ran into a hitch parsing the input in the supplied format, dealing with the comma. Parsing the supplied input files is a totally different problem from the real work, which is summing the columns. Write them separately in the code.
In general you should isolate the "real work" in its own function and have it take parameters as input and returns results as a function return value. The input and output are written separately.
That gives you the added bonus of automating the testing by calling the "work" function with built-in test cases. In this case, it allows you to defer figuring out the parsing. You just pass in test data for now, to get the "work" part working, and then you can come back to parsing the input. Then, when you do need help, it will be specific to "parsing comma separated values" and have nothing to do with why you want them.
② To handle groups of values, you use the array.
This means subscripting or iterating, using loops (or library algorithms) to take what you want to do, written once, and apply it to each value in the array.
Given arrays input and sum, you can accumulate the current row (input) into the running sum with code like this:
for (size_t i = 0; i < COLS; ++i) {
sum[i] += input[i];
}
overall program sketch
open the file
repeat three times:
read a row of input
accumulate the sum with the new input
print the results
Note, as explained in the first topic, that read a row and accumulate the sum are separate functions and separate sub-tasks to figure out. This is called top-down decomposition of a problem.
It's best to use parameters for input and return for output of the function, but for this simple task I'll just use a global variable. Passing/returning is probably harder than the task you are learning! Note though that this is unrealistic in that in real code you would not want to use global variables like this. However, you might turn this into an "object", which you'll learn later.
#include <fstream>
constexpr size_t ROWS = 3;
constexpr size_t COLS = 5;
int input[COLS];
int sum[COLS];
std::ifstream infile;
int main()
{
infile.open("values.txt");
// todo: check for success and feedback to the user if failed
// skipped: zero out the sum array. Global variable start at 0,
// but more generally, you would need to initialize this.
for (size_t row= 0; row < ROWS; ++row) {
read_row();
sum_row();
}
print_results();
}
The sum_row function is what you saw earlier.
Note that with top-down decomposition, you can stub out parts that you will work on later. In particular, you can have read_row return hard-coded result at first, or read from a different format, so you can test the overall program. Then, go back and get that part working for real.
Top-Down Decomposition is critical for any kind of programming project.
Oops... most of your code is useless, and what remains is not really good.
Writing good programs is not a matter of adding C++ instructions one after the other. You must first design the overall structure of your program.
Here you have an input file containing lines of 5 comma separated values and want to compute an array (of size 5) containing the sum of the columns.
Let go from a high level
open the file
loop over the lines
loop 5 times:
read a field up to a comma (or end of the line)
decode that field into an int value
sum that value into an array
close the file
Ok, to be able to sum the values into an array, we will have to define the array before the loop and initialize its elements to 0.
Ok, C++ provide std::ifstream to read a file, std::getline to read a stream up to a delimiter (default being newline), std::istringstream to read the content of a string as an input stream and std::stoi to decode a string representing an int value.
Once this is done, but only after:
the program structure is clearly designed
the required tools from the standard library have been identified
it is possible to sit down in front of your keyboard and start coding.
BTW, this program will never require the declaration of 15 variables a to o nor an array of 15 integers: only int bins[5]...
It could be (tests omitted for brievety):
int bins[5] = {0}; // initializing the first value is enough, others will be 0
std::ifstream in("values.txt");
std::string line;
while (std::getline(in, line)) {
// std::cout << line << '\n'; // uncomment for debug
std::stringstream ss(line);
for(int& val: bins) { // directly loop over the bins array
std::string field;
std::getline(ss, field, ',');
val += std::atoi(field.c_str());
}
}
Of course, for a professional grade (or simply robust) program, every input operation should be followed by a test on the stream...
You can use the std::getline function within the string library to get each comma separated integer.
std::ifstream myfile("values.txt");
for(int i = 0; i < 15; i++)
{
std::string integer_as_string;
std::getline(myfile, integer_as_string, ',');
calc[i] = std::stoi(integer_as_string);
}
myfile.close();
Here we specify that the getline function will read a line of characters in the input until a , character is found. This string is assigned to the integer_as_string variable which will then be converted to an integer and gets assigned to the array.
Also note that i <= 15 will result in undefined behavior. You can further read it here: Wikipedia. And the myfile.close() function was set inside the for loop. This means that in every iteration, you will be closing the file. This is not needed. I think what your looking for is something like this.
std::ifstream myfile("values.txt");
for(int i = 0; i < 15; i++)
{
std::string integer_as_string;
std::getline(myfile, integer_as_string, ',');
calc[i] = std::stoi(integer_as_string);
std::cout << calc[i] << std::endl;
}
myfile.close();
a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];
References:
std::stoi
Why is "using namespace std;" considered bad practice?
First, your array have element with indices from 0 to 14, thus for(int i = 0; i <= 15; i++) should be for(int i = 0; i < 15; i++)
The loop itself might benefit from error-checking. What if file contains less than 15 values?
for(int i = 0; i <= 15; i++)
{
// you want check status of myfile here.
}
myfile >> calc[i] wouldn't work well with commas unless you add comma to a separator class for that stream. Albeit that can be done that's a little large change and one can use getline (see answers here for examples) instead to specify separator.
If you want named variables to refer to element of array, you can make them references and structurally bind them to array (or other tuple-like data structure, e.g. struct, etc.) provided you have access to C++17
int calc[15] = {};
auto& [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o] = calc;
a would become a reference to calc[0], b to calc[1] and so on.

C++: How to read multiple lines from file until a certain character, store them in array and move on to the next part of the file

I'm doing a hangman project for school, and one of the requirements is it needs to read the pictures of the hanging man from a text file. I have set up a text file with the '-' char which means the end of one picture and start of the next one.
I have this for loop set up to read the file until the delimiting character and store it in an array, but when testing I am getting incomplete pictures, cut off in certain places.
This is the code:
string s;
ifstream scenarijos("scenariji.txt");
for(int i = 0; i < 10; i++ ) {
getline(scenarijos, s, '-');
scenariji[i] = s;
}
For the record, scenariji is an array with type of string
And here is an example of the text file:
example
From your example input, it looks like '-' can be part of the input image (look at the "arms" of the hanged man). Unless you use some other, unique character to delimit the images, you won't be able to separate them.
If you know the dimensions of the images, you could read them without searching for the delimiter by reading a certain amount of bytes from the input file. Alternatively, you could define some more complex rules for image termination, e.g. when the '-' character is the only character in the line. For example:
ifstream scenarijos("scenariji.txt");
string scenariji[10];
for (int i = 0; i < 10; ++i) {
string& scenarij = scenariji[i];
while (scenarijos.good()) {
string s;
getline(scenarijos, s); // read line
if (!scenarijos.good() || s == "-")
break;
scenarij += s;
scenarij.push_back('\n'); // the trailing newline was removed by getline
}
}

Reading a truth table in from plain text, translating it to a map<int,list<int>> in C++

I'm writing a file parser for standard C++ (no third-parties like Boost, unfortunately)...
I'm dealing with a situation where I have a plain-text file formatted like this:
1 ..header line 1, unimportant
2 ..header line 2, unimportant
3 ..header line 3, unimportant
4 1 0 0 0 0 0 0 1
5 2 0 1 0 2 1 0 0
...skipping ahead
14 11 1 0 0 0 0 1 1
15 12 0 0 1 0 0 1 2
16 13 2 0 0 0 1 0 0
...etc
(Note: The first column, 1 - 16, are line numbers. The skip ahead is meant to represent the gap of 8 spaces from the start of each line gets shorter as the second column, 1- 13, gets longer and longer numbers.
This text file denotes a truth table whereby items must be grouped by the columns, and each group will be composed of corresponding numbers from the first column. For instance, by the end of parsing this example, a map of type <int, list<int>> should look like (assuming there are no truths between lines 6 and 13):
[1: {11, 13}]
[2: {5, 15}]
[3: {12}]
[4: {5}]
[5: {5,16}]
[6: {14,15}]
[7: {4,14,15}]
In general, the number of columns in the text file can change, meaning the number of groups will change, so this must be accounted for. The number of rows is also variable, but will both will always start at 1 and the columns will not be numbered (but we can do that ourselves).
Now, were I to do this in Java I'd have a working solution rather quickly. However, I've never done work in C++ and am having trouble figuring out how to perform the operations properly, between its different structures and syntax. Despite scouring and finding lots of good guides, my lack of C++ foundation makes it hard to understand even the syntax differences that, I speculate, must be very basic.
Still, I've designed procedure, and it should work according to the following pseudocode:
//Begin Parse
//Create filereader "strmFileIn"
//To get past the first three lines, which will always be needless header info
string dummyLine;
for (i = 1; i <= 3; i++)
getline(strmFileIn, strDummyLine);
//Read first line to get count of how many groups are present
//(Copied from internet: gets the first line and puts the cursor back at its start)
int startPos = strmFileIn.tellg();
string strFirstLine;
getline(strmFileIn, strFirstLine);
strmFileIn.seekg(startPos, std::ios_base::beg);
//Tokenize strFirstLine into Array<int> tempArray
int numGroups = tempArray.size() - 1 //accounting for the row-header column, 1 - 13
//Create map (going to use java syntax, sorry)
Map<int,list<int>> myMap = new Map<int,list<int>>;
//Populate map with ints and empty lists (java again, sorry)
for (int i = 1; i <= numGroups; i++)
myMap.put(i, new List<int>);
//Iterate over lines in the file and appropriately populate the map's lists
while (fileIn != eof)
{
string fileInLine;
getline(strmFileIn, fileInLine);
//Tokenize fileInLine into Array<int> tempFileInArray
int intElemID = tempFileInArray[0];
//Remove element [0] from tempFileInArray (will be the row number, 1 - 13
//Iterate over remaining items in tempFileInArray, affect myMap where necessary
for (int i = 1; int i <= groupNum; i++)
if (tempFileInArray[i] != 0) //is not a strict truth-table, as any nonzero will be a truth
myMap.get[i].add(intElemID);
}
//Remove any entries in myMap with empty lists
//Kill strmFileIn for memory's sake
//End Parse
As you can see, my code is a broken mix of pseudocode and comparable Java I've already figured out. I just don't know how to turn this into C++; even with similar data structures, the syntax is a little daunting to someone with no experience. Is anyone here willing to help me out with it?
I really appreciate any insight.
Your code seems overly complicated, so lets do this one step at a time. Additionally, neither your code nor file format show how many bool columns should exist on each row, so I've ignored that part for this answer.
But first, a tip: In C++, the containers you care about 99.99% of the time are std::unordered_map, std::vector, and in very rare cases, std::map, boost::stable_vector and std::deque. In your case, you have rows with sequential indices, and the data for each row appears to be better stored as a vector of booleans. However, we'll do it your way, with the replacement of std::vector instead of std::list, and std::unordered_map instead of std::map.
This major data structures are mostly obvious:
std::unordered_map<int,std::vector<int>> myMap;
std::ifstream strmFileIn("input_file.txt");
Next your code reads in the first line, then ignores it entirely. I have no idea why, so I'll skip over that. Then, we parse out the lines one by one:
std::string full_current_line;
//for as long as we can read more lines, read them in
while(std::getline(strmFileIn, full_current_line)
{
//make the line into a stream so that we can parse data out
std::stringstream cur_line_stream(full_current_line);
//read in the line identifier
int identifier = 0;
cur_line_stream >> identifier;
//if that failed, abort.
if (!cur_line_stream)
{
//invalid identifer!
std::cerr << "identifier is invalid!\n"; //report
strmFileIn.setstate(std::ios::failbit); //failed to parse the data
break; //do not continue this loop
}
After that, we parse out the data for each row, which is surprisingly simple:
int column = 0;
int is_true = false;
//for each number remaining in the row...
while(cur_line_stream >> is_true)
{
//hooray we read a column!
++column;
if (is_true ==0)
{
//if it's zero, skip it
}
else if (is_true == 1)
{
//get the data for this column, and add this row's identifier
//myMap[column] will create a new empty entry if it didn't exist yet
//NOTE: This syntax only creates when used with map and unordered_map.
// This syntax does NOT create for vector and deque.
//once we have the vector, we push_back the new identifier into it.
myMap[column].push_back(identifier);
}
else
{
//invalid data!
std::cerr << is_true << " is invalid! found on row " << identifier << '\n';
cur_line_stream.setstate(std::ios::failbit); //failed to parse the data
strmFileIn.setstate(std::ios::failbit); //failed to parse the data
break; //do not continue this loop
}
}
}
If you know that groupNum contained the number of bools, you could replace that second while with something more like you already have:
for (int i = 1; int i <= groupNum; i++)
{
cur_line_stream >> is_true;
//if that failed, abort
if (!cur_line_stream)
{
//invalid data!
std::cerr << "data could not be read on row " << identifier << '\n';
cur_line_stream.setstate(std::ios::failbit); //failed to parse the data
strmFileIn.setstate(std::ios::failbit); //failed to parse the data
break; //do not continue this loop
}
else if (is_true == 0)
{
//if it's zero, skip it
}
etc etc etc
Work the other way. Code only in C++ (not in Java and don't think in Java), but start by parsing a small chunk of your syntax. First, code the lexer. Test it. Then code the parser, probably a recursive descent parser, and test it on short simple subelements of your language. Perhaps you'll need some small look-ahead (an easy task, use a std::list<Token>) Keep going up.
Start by formalizing, with pencil and paper, your input language. Could you for instance write a simple BNF grammar for it? (your question does not explain what is the input, it just gives an example)
In C++ parlance: to parse a map<int,list<int>> you certainly need to be able to parse int and list<int>. So write first the parsers for these.
As commented by Mooing Duck, your input language (which you did not define, just gave an example) seems simple enough to avoid most of this. But still, the idea is the same, think directly in C++ and start by reading a simple subpart of the input. Test your code. When that works, increase the part that is accepted. Repeat all this.
Here's a very simple solution that uses nothing but C++ and standard libraries. It just reads line by line and pulls each element out of the line with stream extraction using operator>>.
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <list>
int main(int argc, char* argv[])
{
// Parse command line
if( argc != 2 )
return 1;
std::fstream fin(argv[1]);
if( !fin.good() )
{
std::cerr << "Error opening file for reading: " << argv[1] << std::endl;
return 1;
}
// Skip first three lines
std::string line;
for( int i=0; i<3; ++i )
{
std::getline(fin, line);
}
// Read each line
std::map<int, std::list<int> > hits;
while( std::getline(fin, line) )
{
// Extract each element from the line
std::stringstream sstr(line);
// Read line number from first column
int linenum = 0;
sstr >> linenum;
// Interpret remaining columns as truth values
bool truth;
int col=1;
while( sstr >> truth )
{
// Store position in map if true
if( truth )
{
hits[col].push_back(linenum);
}
col++;
}
}
// Print results
std::map<int, std::list<int> >::const_iterator col_iter;
for( col_iter = hits.begin(); col_iter != hits.end(); ++col_iter )
{
std::cout << "[" << col_iter->first << ": {";
std::list<int>::const_iterator line_iter;
for( line_iter = col_iter->second.begin(); line_iter != col_iter->second.end(); ++line_iter )
{
std::cout << *line_iter << " ";
}
std::cout << "} ]" << std::endl;
}
return 0;
}

read in values and store in list in c++

i have a text file with data like the following:
name
weight
groupcode
name
weight
groupcode
name
weight
groupcode
now i want write the data of all persons into a output file till the maximum weight of 10000 kg is reached.
currently i have this:
void loadData(){
ifstream readFile( "inFile.txt" );
if( !readFile.is_open() )
{
cout << "Cannot open file" << endl;
}
else
{
cout << "Open file" << endl;
}
char row[30]; // max length of a value
while(readFile.getline (row, 50))
{
cout << row << endl;
// how can i store the data into a list and also calculating the total weight?
}
readFile.close();
}
i work with visual studio 2010 professional!
because i am a c++ beginner there could be is a better way! i am open for any idea's and suggestions
thanks in advance!
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <limits>
struct entry
{
entry()
: weight()
{ }
std::string name;
int weight; // kg
std::string group_code;
};
// content of data.txt
// (without leading space)
//
// John
// 80
// Wrestler
//
// Joe
// 75
// Cowboy
int main()
{
std::ifstream stream("data.txt");
if (stream)
{
std::vector<entry> entries;
const int limit_total_weight = 10000; // kg
int total_weight = 0; // kg
entry current;
while (std::getline(stream, current.name) &&
stream >> current.weight &&
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n') && // skip the rest of the line containing the weight
std::getline(stream, current.group_code))
{
entries.push_back(current);
total_weight += current.weight;
if (total_weight > limit_total_weight)
{
break;
}
// ignore empty line
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
else
{
std::cerr << "could not open the file" << std::endl;
}
}
Edit: Since you wannt to write the entries to a file, just stream out the entries instead of storing them in the vector. And of course you could overload the operator >> and operator << for the entry type.
Well here's a clue. Do you see the mismatch between your code and your problem description? In your problem description you have the data in groups of four lines, name, weight, groupcode, and a blank line. But in your code you only read one line each time round your loop, you should read four lines each time round your loop. So something like this
char name[30];
char weight[30];
char groupcode[30];
char blank[30];
while (readFile.getline (name, 30) &&
readFile.getline (weight, 30) &&
readFile.getline (groupcode, 30) &&
readFile.getline (blank, 30))
{
// now do something with name, weight and groupcode
}
Not perfect by a long way, but hopefully will get you started on the right track. Remember the structure of your code should match the structure of your problem description.
Have two file pointers, try reading input file and keep writing to o/p file. Meanwhile have a counter and keep incrementing with weight. When weight >= 10k, break the loop. By then you will have required data in o/p file.
Use this link for list of I/O APIs:
http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx
If you want to struggle through things to build a working program on your own, read this. If you'd rather learn by example and study a strong example of C++ input/output, I'd definitely suggest poring over Simon's code.
First things first: You created a row buffer with 30 characters when you wrote, "char row[30];"
In the next line, you should change the readFile.getline(row, 50) call to readFile.getline(row, 30). Otherwise, it will try to read in 50 characters, and if someone has a name longer than 30, the memory past the buffer will become corrupted. So, that's a no-no. ;)
If you want to learn C++, I would strongly suggest that you use the standard library for I/O rather than the Microsoft-specific libraries that rplusg suggested. You're on the right track with ifstream and getline. If you want to learn pure C++, Simon has the right idea in his comment about switching out the character array for an std::string.
Anyway, john gave good advice about structuring your program around the problem description. As he said, you will want to read four lines with every iteration of the loop. When you read the weight line, you will want to find a way to get numerical output from it (if you're sticking with the character array, try http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/, or try http://www.cplusplus.com/reference/clibrary/cstdlib/atof/ for non-whole numbers). Then you can add that to a running weight total. Each iteration, output data to a file as required, and once your weight total >= 10000, that's when you know to break out of the loop.
However, you might not want to use getline inside of your while condition at all: Since you have to use getline four times each loop iteration, you would either have to use something similar to Simon's code or store your results in four separate buffers if you did it that way (otherwise, you won't have time to read the weight and print out the line before the next line is read in!).
Instead, you can also structure the loop to be while(total <= 10000) or something similar. In that case, you can use four sets of if(readFile.getline(row, 30)) inside of the loop, and you'll be able to read in the weight and print things out in between each set. The loop will end automatically after the iteration that pushes the total weight over 10000...but you should also break out of it if you reach the end of the file, or you'll be stuck in a loop for all eternity. :p
Good luck!

Need to write specific lines of a text into a new text

I have numerical text data lines ranging between 1mb - 150 mb in size, i need to write lines of numbers related to heights, for example: heights=4 , new text must include lines: 1,5,9,13,17,21.... consequentially.
i have been trying to find a way to do this for a while now, tried using a list instead of vector which ended up with compilation errors.
I have cleaned up the code as advised. It now writes all lines sample2 text, all done here. Thank you all
I am open to method change as long as it delivers what i need, Thank you for you time and help.
following is what i have so far:
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <vector>
using namespace std;
int h,n,m;
int c=1;
int main () {
cout<< "Enter Number Of Heights: ";
cin>>h;
ifstream myfile_in ("C:\\sample.txt");
ofstream myfile_out ("C:\\sample2.txt");
string line;
std::string str;
vector <string> v;
if (myfile_in.is_open()) {
myfile_in >> noskipws;
int i=0;
int j=0;
while (std::getline(myfile_in, line)) {
v.push_back( line );
++n;
if (n-1==i) {
myfile_out<<v[i]<<endl;
i=i+h;
++j;
}
}
cout<<"Number of lines in text file: "<<n<<endl;
}
else cout << "Unable to open file(s) ";
cout<< "Reaching here, Writing one line"<<endl;
system("PAUSE");
return 0;
}
You need to use seekg to set the position at the beginning of the file, once you have read it (you have read it once, to count the lines (which I don't think you actually need, as this size is never used, at least in this piece of code)
And what is the point if the inner while? On each loop, you have
int i=1;
myfile_out<<v[i]; //Not writing to text
i=i+h;
So on each loop, i gets 1, so you output the element with index 1 all the time. Which is not the first element, as indices start from 0. So, once you put seekg or remove the first while, your program will start to crash.
So, make i start from 0. And get it out of the two while loops, right at the beginning of the if-statement.
Ah, the second while is also unnecessary. Leave just the first one.
EDIT:
Add
myfile_in.clear();
before seekg to clear the flags.
Also, your algorithm is wrong. You'll get seg fault, if h > 1, because you'll get out of range (of the vector). I'd advise to do it like this: read the file in the while, that counts the lines. And store each line in the vector. This way you'll be able to remove the second reading, seekg, clear, etc. Also, as you already store the content of the file into a vector, you'll NOT lose anything. Then just use for loop with step h.
Again edit, regarding your edit: no, it has nothing to do with any flags. The if, where you compare i==j is outside the while. Add it inside. Also, increment j outside the if. Or just remove j and use n-1 instead. Like
if ( n-1 == i )
Several things.
First you read the file completely, just to count the number of lines,
then you read it a second time to process it, building up an in memory
image in v. Why not just read it in the first time, and do everything
else on the in memory image? (v.size() will then give you the number
of lines, so you don't have to count them.)
And you never actually use the count anyway.
Second, once you've reached the end of file the first time, the
failbit is set; all further operations are no-ops, until it is reset.
If you have to read the file twice (say because you do away with v
completely), then you have to do myfile_in.clear() after the first
loop, but before seeking to the beginning.
You only test for is_open after having read the file once. This test
should be immediately after the open.
You also set noskipws, although you don't do any formatted input
which would be affected by it.
The final while is highly suspect. Because you haven't done the
clear, you probably never enter the loop, but if you did, you'd very
quickly start accessing out of bounds: after reading n lines, the size
of v will be n, but you read it with index i, which will be n * h.
Finally, you should explicitly close the output file and check for
errors after the close, just in case.
It's not clear to me what you're trying to do. If all you want to do is
insert h empty lines between each existing line, something like:
std::string separ( h + 1, '\n' );
std::string line;
while ( std::getline( myfile_in, line ) ) {
myfile_out << line << separ;
}
should do the trick. No need to store the complete input in memory.
(For that matter, you don't even have to write a program for this.
Something as simple a sed 's:$:\n\n\n\n:' < infile > outfile would do
the trick.)
EDIT:
Reading other responses, I gather that I may have misunderstood the
problem, and that he only wants to output every h-th line. If this is
the case:
std::string line;
while ( std::getline( myfile_in, line ) ) {
myfile_out << line << '\n';
for ( int count = h - 1; h > 0; -- h ) {
std::getline( myfile_in, line );
// or myfile_in.ignore( INT_MAX, '\n' );
}
}
But again, other tools seem more appropriate. (I'd follow thiton's
suggestion and use AWK.) Why write a program in a language you don't
know well when tools are already available to do the job.
If there is no absolutely compelling reason to do this in C++, you are using the wrong programming language for this. In awk, your whole program is:
{ if ( FNR % 4 == 1 ) print; }
Or, giving the whole command line e.g. in sh to filter lines 1,5,9,13,...:
awk '{ if ( FNR % 4 == 1 ) print; }' a.txt > b.txt