Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I'm trying to read in from a text file in C++ for a Testing Harness. The idea is that I have to read in the 5 values of dice and put it into an array, and then read in a string, run through the program, then read in 5 more values of dice and put those into an array, and then read in a string, and run the program, etc.
The text file looks like this:
1 1 2 1 1 Aces
2 2 4 5 6 Twos
3 3 3 2 2 FullHouse
1 2 3 4 4 SmallStraight
2 3 4 5 6 LargeStraight
6 6 6 6 6 Sixes
What would be the best way using ifstream to search the file for 1 1 2 1 1 and put those values into an array, and then read in "Aces" into a string, and then go to the next line and do the same with the values 2 2 4 5 6 and the string "Twos."
One possible and also C++-style and object oriented approach is, to put the data and operations on the data in a tiny claas.
So let's create a small struct containing an array with 5 integers and a std::string.
For that class, we additionally overwrite the 'inserter' and 'extractor' operator. Then we can use this class for input and output in the same way like for an integral data type.
The inserter operator is ultra simple an just puts the 5 integers and the text in an std:ostream. The exteractor operator is some lines more and basically works like this:
Read a complete line with std::getline
Put the read line in an std::istringstream. With that we can easily extract further data
Then we use the std::istream_iterator and copy exact 5 integers from the std::istringstream. And at the end, we read the text.
This is overall not so complicated.
Then, we can simply call in main the standard IO mechanisms.
Please see the example below:
#include <iostream>
#include <sstream>
#include <iterator>
#include <string>
#include <array>
#include <algorithm>
std::istringstream sourceFile{R"(1 1 2 1 1 Aces
2 2 4 5 6 Twos
3 3 3 2 2 FullHouse
1 2 3 4 4 SmallStraight
2 3 4 5 6 LargeStraight
6 6 6 6 6 Sixes)"
};
constexpr size_t MaxValues = 5U;
// Proxy class for reading one line of given format
struct PData {
// The Array for the values
std::array<int, MaxValues> values{};
// The description
std::string text{};
// Overwrite extractor operator
friend std::istream& operator >> (std::istream& is, PData& pdata) {
// Read complete line
std::string line{};
if (std::getline(is, line)) {
// Put line in istringstream
std::istringstream iss(line);
// Extract all integers
std::copy_n(std::istream_iterator<int>(iss), MaxValues, pdata.values.begin());
// Read the text
iss >> pdata.text;
}
return is;
}
// Overwrite inserter operator
friend std::ostream& operator << (std::ostream& os, const PData& pdata) {
std::copy_n(pdata.values.begin(), 5, std::ostream_iterator<int>(os, "\n"));
return os << pdata.text << "\n";
}
};
int main() {
PData p{};
// Read lines and split them
while (sourceFile >> p) {
// Some debug output (Not necessary)
std::cout << p << "\n\n";
// Now do with p whatever you want
}
return 0;
}
Please note: Since I do have no files on SO, I use the std::istringstream as input file. You can of course use any other std::ifstream.
I just thought I would help you out and give you the entire solution. You remind me of myself back when I was just starting out. I've put in some comments as well.
You can read more about c++ file handling here.
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
// Struct to hold each "line" of the data.
struct Info {
static const size_t DICE_LENGTH = 5;
int dice_values[DICE_LENGTH];
std::string description;
};
/**
* #brief Creates the sample file (assuming it doesn't already exist)
*/
void createFile() {
std::ofstream file ("file.txt");
if (file.is_open()) {
file << "1 1 2 1 1 Aces\n";
file << "2 2 4 5 6 Twos\n";
file << "3 3 3 2 2 FullHouse\n";
file << "1 2 3 4 4 SmallStraight\n";
file << "2 3 4 5 6 LargeStraight\n";
file << "6 6 6 6 6 Sixes";
}
else {
throw "Error creating file";
}
}
int main()
{
createFile();
std::vector<Info> data;
std::ifstream file("file.txt");
if (file.is_open()) {
Info info;
while (!file.eof()) {
// read all the dice values (at most = DICE_LENGTH)
for (size_t i = 0; i < Info::DICE_LENGTH; ++i) {
file >> info.dice_values[i];
}
// read the String
std::getline(file, info.description);
data.push_back(info);
}
}
else {
std::cerr << "Error opening the file for reading.\n";
return 1;
}
// display the data the was read from the file.
for (const auto &d : data) {
// Dice values.
for (const auto &dice : d.dice_values) {
std::cout << dice << " ";
}
// String value.
std::cout << d.description << std::endl;
}
return 0;
}
Output:
1 1 2 1 1 Aces
2 2 4 5 6 Twos
3 3 3 2 2 FullHouse
1 2 3 4 4 SmallStraight
2 3 4 5 6 LargeStraight
6 6 6 6 6 Sixes
Related
I'm trying to read in this text file:
8 4 4 6 1
8 4 4 6 2
8 4 4 6 3
8 4 4 6 4
8 4 4 6 5
8 4 4 6 6
8 4 4 6 7
8 4 4 6 8
11 4 4 6 3
15 11 13
7 2 1 4 4
9 4 3 9 9
8 2 1 5 4
10 1 2 3 4 6 1
6 1 1 2 5 3 2
13 1 1 2 10 3 8
11 2 11 10 7
And printing it exactly as shown to the console (to make sure I got every input).
However, for some reason my code crashes after reading in the first line. I can't even terminate the debugger.
Here's my code:
while(getline(inFile, buffer)){
buffer2 = strdup(buffer.c_str());
line = strtok(buffer2, " ");
size = atoi(line);
cout << size << " ";
while(line!=NULL){
line = strtok(NULL, " ");
cout << line << " ";
}
cout << "~~~~~~~~~" << endl;
}
If you are going to use C++ you should take advantage of that, use string streams:
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std; //for sample purposes, should not be used
int main() {
int temp, count = 0, sum = 0, total = 0;
string buffer;
ifstream myFile("in.txt");
if (!myFile.is_open())
cout << "No file" << endl;
else{
while(getline(myFile, buffer)){
sum = 0;
stringstream ss(buffer);
while(ss >> temp){
count++; //number count
sum += temp; //line sum
cout << temp << " ";
}
total += sum; //total sum
cout << endl << "count: " << count << endl
<< "sum: " << sum << endl << "total: " << total << endl << endl;
}
myFile.close();
}
cout << "~~~~~~~~~" << endl;
}
You are leaking the memory allocated by strdup(). You need to call free() when you are done using buffer2.
But more importantly, strtok() returns NULL when there are no more tokens to return. But it is undefined behavior to pass a NULL char* pointer to operator<<. Your while loop is doing exactly that when it reaches the end of each line, so anything could happen, including crashing.
Try this instead:
while (getline(inFile, buffer)) {
buffer2 = strdup(buffer.c_str());
if (buffer2 != NULL) {
line = strtok(buffer2, " ");
while (line != NULL) {
size = atoi(line);
cout << size << " ";
line = strtok(NULL, " ");
}
free(buffer2);
}
cout << "~~~~~~~~~" << endl;
}
That being said, why are you using strdup(), strtok(), and atoi() at all? You are writing C++ code, you should C++ semantics instead of C semantics. For example, you can use std::istringstream instead, eg:
while (getline(inFile, buffer)) {
istringstream iss(buffer);
while (iss >> size) {
cout << size << " ";
}
cout << "~~~~~~~~~" << endl;
}
As always, there are many possible solutions. I would like to show an additional one. This is using more modern C++ elements, mainly from the algorithm and iterator library.
So, what will we do?
First we read each line as a std::string in a simple for loop with std::getline. Then we will put the line again in a std::istringstream so that we can take advantage of C++ iterator: std::istream_iterator.
This iterator will iterate over the elements in the string and extract all integers. It is like calling the extractor operator ( >> ) for all elements in the line string.
We use the iterator in the so called range constructor of os a std::vector. This inplace created vector, will be added to the destiantion data. So, as a result, we will get vector of vector of int: A 2-dimensional vector.
For debug purposes, we copy each row of intes to std::cout.
Please note that we do really need only very few and very simple statements to fulfill the task.
Please check.
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
#include <iterator>
std::istringstream sourceFile{R"(8 4 4 6 1
8 4 4 6 2
8 4 4 6 3
8 4 4 6 4
8 4 4 6 5
8 4 4 6 6
8 4 4 6 7
8 4 4 6 8
11 4 4 6 3
15 11 13
7 2 1 4 4
9 4 3 9 9
8 2 1 5 4
10 1 2 3 4 6 1
6 1 1 2 5 3 2
13 1 1 2 10 3 8
11 2 11 10 7)"};
int main()
{
// Here we will store the resulting int values
std::vector<std::vector<int>> data{};
for (std::string line{}; std::getline(sourceFile, line); ) {
// Split the line into integers and add to target array
std::istringstream iss(line);
data.emplace_back(std::vector<int>(std::istream_iterator<int>(iss), {}));
}
// Now all data is in our vector of vector of int
// Show read data on screen
std::for_each(data.begin(), data.end(), [](const std::vector<int>& v){
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << "\n";});
return 0;
}
Please note. I do not have files on SO. So I used a std::istringstream as input stream. You may of course exchange it with any other std::ftream
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I've got a text file which contains several lines of integers, each integer is separated by a space, I want to read these integers into an array, where each new line is the first dimension of the array, and every integer on that line is saved into the second dimension.
My text file looks something like this:
0 1 2 3 4 5 6 7 8 9
9 0 1 2 3 4 5 6 7 8
8 9 0 1 2 3 4 5 6 7
7 8 9 0 1 2 3 4 5 6
6 7 8 9 0 1 2 3 4 5
5 6 7 8 9 0 1 2 3 4
4 5 6 7 8 9 0 1 2 3
3 4 5 6 7 8 9 0 1 2
2 3 4 5 6 7 8 9 0 1
So here's what i tried so far, but it looks like a mess
string array[30][30]; //row, column
ifstream myfile("numbers.txt");
int row = 0;
int col = 0;
while(!myfile.eof())
{
//Extract columns
while(getline(myfile, array[row][col]),!'\n')
{
getline(myfile,array[row][col],' ');
col++;
}
//Extract rows
// getline(myfile,array[row][col],'\n');
// row++;
cout<< row << '\t' << array[row][col] << "\n";
}
while(!myfile.eof()) is rarely a good idea. When you've read your last line, that condition will still evaluate to true. eof() will only be set once you've tried to read beyond the last character in the file. Also, string array[30][30] is a hardcoded 30x30 C style array that doesn't fit your data. Instead, use the C++ container std::vector (that can be nested in as many dimensions as you'd like) to dynamically add numbers.
Assuming that you don't have blank lines in numbers.txt you could do like this:
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <stdexcept>
std::vector<std::vector<int>> get_2d_array_of_ints_from_stream(std::istream& is) {
std::vector<std::vector<int>> return_value;
std::string line;
while(std::getline(is, line)) { // if this fails, EOF was found or there was an error
std::istringstream iss(line); // put the line in a stringstream to extract numbers
int value; // temporary used for extraction
std::vector<int> line_values; // all values on this line
while(iss >> value) // extract like when reading an int from std::cin
line_values.push_back(value); // put the value in the 1D (line) vector
// check that all lines have the same amount of numbers
if(return_value.size() && return_value[0].size()!=line_values.size())
throw std::runtime_error("file format error");
return_value.emplace_back(std::move(line_values)); // move this line's vector<int>
// to the result_value
}
return return_value;
}
int main() {
if(std::ifstream is{"numbers.txt"}; is) {
try {
// auto arr2d = get_2d_array_of_ints_from_stream(is);
// would be the same as:
std::vector<std::vector<int>> arr2d = get_2d_array_of_ints_from_stream(is);
std::cout << "Got a " << arr2d[0].size() << "x" << arr2d.size() << " array\n";
for(const std::vector<int>& line_values : arr2d) {
for(int value : line_values) {
std::cout << " " << value;
}
std::cout << "\n";
}
std::cout << "--\n";
// or you can use the subscript style of arrays
for(size_t y = 0; y < arr2d.size(); ++y) {
for(size_t x = 0; x < arr2d[y].size(); ++x) {
std::cout << " " << arr2d[y][x];
}
std::cout << "\n";
}
} catch(const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << "\n";
}
}
}
I have a 2d vector where the rows are unequal. I have been trying to delete the first column but have no luck look at previous stackoverflow posts.
example data:
1 2 4 5 6 6
1 2 3 4 6 6 8
Code to read in data:
myfile.open("test.txt");
if(myfile.is_open())
{
while(getline(myfile, line)){
//cout << "This line: ";
if(line != "")
{
istringstream is(line);
sortVec.push_back(std::vector<int>( std::istream_iterator<int>(is),
std::istream_iterator<int>() ) );
}
}
}
else
{
cout << "Myfile is not open" << endl;
}
myfile.close();
When I try to erase the first column using std:vector:
int columnIndex = 0;
for(auto& row:sortVec){
row.erase(next(row.begin(), columnIndex));
}
I get a segmentation fault.
I have tried the following stackoverflow posts as well.
How to delete column in 2d vector, c++
Additionally when I create a vector manually everything works perfect so I am lost at the moment.
Desired output:
2 4 5 6 6
2 3 4 6 6 8
The answer was that don't forget to check empty lines when using getline. The last line of my file was empty.
I have this code which contains a class and a main function:
class Employee {
int m_id;
string m_name;
int m_age; public:
Employee(int id, string name, int age) :m_id(id), m_name(name), m_age(age) {}
friend ostream& operator<<(ostream& os, const Employee& emp)
{
os << emp.m_id << " " << emp.m_name << " " << emp.m_age
<< endl;
return os;
}
};
int main() {
const int Emp_Num = 3;
fstream fs("dataBase.txt", ios::out);
if (!fs) {
cerr << "Failed opening file. Aborting.\n";
return -1;
}
Employee* list[Emp_Num] =
{ new Employee(1234, "Avi", 34),
new Employee(11111, "Beni", 24),
new Employee(5621, "Reut", 26) };
for (int i = 0; i < Emp_Num; i++)
{
fs << (*list[i]);
delete list[i];
}
fs.close();
fs.open("dataBase.txt");
if (!fs) {
cerr << "Failed opening file. Aborting.\n";
return -1;
}
fs.seekg(4);
string strRead;
fs >> strRead;
cout << strRead << endl;
fs.seekg(6, ios::cur);
fs >> strRead;
cout << strRead << endl;
fs.seekg(-9, ios::end);
fs >> strRead;
cout << strRead << endl;
}
Here is how I understand it, after the first file open and close, the file dataBase.txt should look like this:
1234 Avi 34
11111 Beni 24
5621 Reut 26
My problem is with the reading and output to the console.
After I open the file, the pointer of my current position is at the first byte, which is the 1 before the 1234.
I seek 4 from the beginning of the file,
so my pointer should be at (before) the space between the 1234 and Avi.
Now I get the next string into my string variable strRead,
now strRead contains "Avi" and the pointer should be between the i of Avi and the space after it.
Now i seek 6 from my current position,
by my count those are the 6 bytes i pass through:
Space
3
4
Line break (return)
1
1
So my pointer should be at the second line, after the two first ones.
I mean like this:
11|111 Beni 24
Now I get a string to strRead, by my understanding of the code strRead should now contain "111", instead, for some reason, it contains and output later "1111".
Could someone explain me why does it work that way?
There is no character between the first line drop and the first letter of the second line, so it should count as only 1 byte...
I did the following test:
I have run the second part of your code (which read from file) on a file with the text:
1234 Avi 34 11111 Beni 24 5621 Reut 26
So, I have replaced end of lines with spaces, and the code print to console output the expected result 111. I then began to be suspicious about seek skipping end of lines.
Then I changed the code instead (without file modifications) and worked with the file in binary mode:
//...
fstream fs("dataBase.txt", ios::out | ios::binary);
//...
fs.open("dataBase.txt", ios::in | ios::binary );
//...
Again the result was the expected: 111.
What change in both cases?
Well, in plain text (not in binary mode) the end of line are actually 2 chars (this can vary for other platforms, I'm reproducing this on Windows): \r and \n. Thats why you're reading four ones (1111) instead three (111).
Counting 6 positions from the space after Avi:
A v i _ 3 4 \r \n 1 1 1 1 1
^
1 2 3 4 5 6 7 8
In the first test I performed, the a space (only one character) replaced two of them.
A v i _ 3 4 _ 1 1 1 1 1
^
1 2 3 4 5 6 7 8
And in binary mode both of characters are represented as a single one unit to read(I don't have investigated if this is platform dependant).
A v i _ 3 4 B 1 1 1 1 1
^
1 2 3 4 5 6 7 8
B stands here for some binary code.
I am being given input in the form of:
(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)
(2,1,6) (2,2,7) (2,5,8)
(3,0,9) (3,3,10) (3,4,11) (3,5,12)
(4,1,13) (4,4,14)
(7,6,15)
where I have to remember the amount of triples there are. I wrote a quick testing program to try read the input from cin and then split string up to get the numbers out of the input. The program doesn't seem to read all the lines, it stops after (1,1,5) and prints out a random 7 afterwards
I created this quick testing function for one of the functions I am trying to create for my assignment:
int main ()
{
string line;
char * parse;
while (getline(cin, line)) {
char * writable = new char[line.size() + 1];
copy (line.begin(), line.end(), writable);
parse = strtok (writable," (,)");
while (parse != NULL)
{
cout << parse << endl;
parse = strtok (NULL," (,)");
cout << parse << endl;
parse = strtok (NULL," (,)");
cout << parse << endl;
parse = strtok (NULL," (,)");
}
}
return 0;
}
Can someone help me fix my code or give me a working sample?
You can use this simple function:
istream& read3(int& a, int& b, int& c, istream& stream = cin) {
stream.ignore(INT_MAX, '(');
stream >> a;
stream.ignore(INT_MAX, ',');
stream >> b;
stream.ignore(INT_MAX, ',');
stream >> c;
stream.ignore(INT_MAX, ')');
return stream;
}
It expects the stream to start at a (, so it skips any characters and stops after the first ( it sees. It reads in an int into a which is passed by reference (so the outside a is affected by this) and then reads up to and skips the first comma it sees. Wash, rinse, repeat. Then after reading the third int in, it skips the closing ), so it is ready to do it again.
It also returns an istream& which has operator bool overloaded to return false when the stream is at its end, which is what breaks the while loop in the example.
You use it like this:
// don't forget the appropriate headers...
#include <iostream>
#include <sstream>
#include <string>
int a, b, c;
while (read3(a, b, c)) {
cout << a << ' ' << b << ' ' << c << endl;
}
That prints:
8 7 15
0 0 1
0 3 2
0 6 3
1 0 4
1 1 5
2 1 6
2 2 7
2 5 8
3 0 9
3 3 10
3 4 11
3 5 12
4 1 13
4 4 14
7 6 15
When you give it your input.
Because this is an assignment, I leave it to you to add error handling, etc.
I've written a blog 9 days back exactly to parse such inputs:
Playing around with Boost.Spirit - Parsing integer triplets
And you can see the output here for your input : http://ideone.com/qr4DA