I am trying to read a bunch of strings from a text file and saving the characters in a 2D array. The following is my code:
char** fileReader(char* fileName){
ifstream treeFile;
treeFile.open(fileName);
string line;
vector<string> fileContents;
int rows=0, columns=0;
while (getline(treeFile, line )){
fileContents.push_back(line);
rows++;
}
columns = fileContents.at(0).length();
char** fileContentsArr;
fileContentsArr = new char*[rows];
for (int x=0; x < rows; x++){
fileContentsArr[x] = new char[columns];
for (int y=0; y < columns; y++){
fileContentsArr[x][y]= fileContents.at(x)[y];
}
}
treeFile.close();
return fileContentsArr;
}
Output should be:
TTTTTTTT
TTTTTTTT
TTTTFFFT
TTTTTTFF
FFFFTTFF
FFFFTTFF
FFFFTTTT
FFFFTTTF
But instead I am getting only the first 7 characters from each line and only the first 7 strings.
Actual output:
TTTTTTT
TTTTTTT
TTTTFFF
TTTTTTF
FFFFTTF
FFFFTTF
FFFFTTT
What am I doing wrong?
You can use the STL to do almost everything you need here:
vector<string> fileReader(char* fileName){
ifstream treeFile(fileName);
vector<string> fileContents(
(std::istream_iterator<string>(treeFile)),
std::istream_iterator<string>());
return fileContents;
}
This creates the vector using its two-iterator constructor, with the first iterator reading from treeFile and producing strings. The second iterator (default-constructed) signifies the end of the file.
Fixed version:
// Changed return type:
std::vector<std::string> fileReader(char* fileName){
ifstream treeFile;
treeFile.open(fileName);
string line;
std::vector<std::string> fileContents;
/// int rows=0, columns=0;
while (getline(treeFile, line )){
fileContents.push_back(line);
rows++;
}
// Cut off rest of code, instead:
return fileContents;
}
If you want to access a character, you can use
std::vector<std::string> data = fileReader("file.txt");
char value = data[3][2];
as intended.
Related
I'm having slight trouble creating a 2D Vector of String that's created by reading values from a text file. I initially thought I needed to use an array. however I've come to realise that a vector would be much more suited to what I'm trying to achieve.
Here's my code so far:
I've initialised the vector globally, but haven't given it the number of rows or columns because I want that to be determined when we read the file:
vector<vector<string>> data;
Test data in the file called "test" currently looks like this:
test1 test2 test3
blue1 blue2 blue3
frog1 frog2 frog3
I then have a function that opens the file and attempts to copy over the strings from text.txt to the vector.
void createVector()
{
ifstream myReadFile;
myReadFile.open("text.txt");
while (!myReadFile.eof()) {
for (int i = 0; i < 5; i++){
vector<string> tmpVec;
string tmpString;
for (int j = 0; j < 3; j++){
myReadFile >> tmpString;
tmpVec.push_back(tmpString);
}
data.push_back(tmpVec);
}
}
}
However, when I attempt to check the size of my vector in my main function, it returns the value '0'.
int main()
{
cout << data.size();
}
I think I just need a pair of fresh eyes to tell me where I'm going wrong. I feel like the issues lies within the createVector function, although I'm not 100% sure.
Thank you!
You should use std::getline to get the line of data first, then extract each string from the line and add to your vector. This avoids the while -- eof() issue that was pointed out in the comments.
Here is an example:
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
typedef std::vector<std::string> StringArray;
std::vector<StringArray> data;
void createVector()
{
//...
std::string line, tempStr;
while (std::getline(myReadFile, line))
{
// add empty vector
data.push_back(StringArray());
// now parse the line
std::istringstream strm(line);
while (strm >> tempStr)
// add string to the last added vector
data.back().push_back(tempStr);
}
}
int main()
{
createVector();
std::cout << data.size();
}
Live Example
I have a txt file with a lot of things in it.
The lines have this pattern: 6 spaces then 1 int, 1 space, then a string.
Also, the 1st line has the amount of lines that the txt has.
I want to put the integers in an array of ints and the string on an array of strings.
I can read it and put it into an array , but only if I'm considering the ints as chars and putting into one array of strings.When I try to separate things I have no idea on how I'd do it. Any ideas?
The code I used for putting everything in an array was this:
int size()
{
ifstream sizeX;
int x;
sizeX.open("cities.txt");
sizeX>>x;
return x;
};
int main(void)
{
int size = size();
string words[size];
ifstream file("cities.txt");
file.ignore(100000,'\n');
if(file.is_open())
{
for(int i=0; i<size; i++)
{
getline(file,words[i]);
}
}
}
Just to start I'm going to provide some tips about your code:
int size = size();
Why do you need to open the file, read the first line and then close it? That process can be done opening the file just once.
The code string words[size]; is absolutely not legal C++. You cannot instantiate a variable-length-array in C++. That C feature has been not included in C++ standard (some ref). I suggest you to replace with std::vector, which is more C++ code.
Here I write a snippet of function which perform what you need.
int parse_file(const std::string& filename,
std::vector<std::string>* out_strings,
std::vector<int>* out_integers) {
assert(out_strings != nullptr);
assert(out_integers != nullptr);
std::ifstream file;
file.open(filename, std::ios_base::in);
if (file.fail()) {
// handle the error
return -1;
}
// Local variables
int num_rows;
std::string line;
// parse the first line
std::getline(file, line);
if (line.size() == 0) {
// file empty, handle the error
return -1;
}
num_rows = std::stoi(line);
// reserve memory
out_strings->clear();
out_strings->reserve(num_rows);
out_integers->clear();
out_integers->reserve(num_rows);
for (int row = 0; row < num_rows; ++row) {
// read the line
std::getline(file, line);
if (line.size() == 0) {
// unexpected end of line, handle it
return -1;
}
// get the integer
out_integers->push_back(
std::stoi(line.substr(6, line.find(' ', 6) - 6)));
// get the string
out_strings->push_back(
line.substr(line.find(' ', 6) + 1, std::string::npos));
}
file.close();
return 0;
}
You can definitely improved it, but I think it's a good point where to start.
The last suggest I can give you, in order to improve the robustness of your code, you can match each line with a regular expression. In this way you can be sure your line is formatted exactly how you need.
For example:
std::regex line_pattern("\\s{6}[0-9]+\\s[^\\n]+");
if (std::regex_match(line, line_pattern) == false) {
// ups... the line is not formatted how you need
// this is an error
}
I am trying to read in the first 7 chars of a file named "board.txt" into a vector<'char> but I am having issues for some reason. I am not too familiar with C++ so any advice would be appreciated, here is the code I have so far
//rack
int charCount = 0;
char ch;
ifstream rackIn("board.txt");
while(rackIn.get(ch) && charCount < 7){
this->getMyRack().push_back(ch);
}
And here is the function getMyRack used in the code above:
vector<char> board::getMyRack(){
return this->myRack;
}
myRack is a char vector
I tried to test this in my main using this:
for (int i = 0; i < test->getMyRack().size(); ++i){
cout << test->getMyRack().at(i);
}
but it does not output anything, why are the chars i am reading in not being added into my char vectors?
Because you don't put char in your vector. Your function getMyRack() returns vector but not address of your vector. You can add method to your class board for adding char, for example:
void board::addChar(char c){
this->myRack.push_back(c);
}
And then call this function:
while(rackIn.get(ch) && charCount < 7){
this->addChar(ch);
}
Or change the return type of your function.
read line one or (how much lines required) from file to a string
create substring of 7 chars from beginning
std::ifstream file("board.txt");
std::string str;
// to read single line
std::getline(file, str);
// to read 7 chars
str= str.substr(0,7);
vector<char> char_buf;
for(size_t i =0; i <= str.size();i++)
{
char_buf.push_back(str[i])
}
// use the char_buf
easier or second way is use
#include<fstream> // for ifstream
#include <cstdlib> // for exit()
std::string file_name ="board.txt";
std::ifstream input_stream;
std::vector<char> char_buf;
input_stream.open(file_name);
if(input_stream.fail()) { exit(0);}
int char_no=0;
while(i<=7)
{
char c = input_stream.get();
char_buf.push_back(c);
i++;
}
// use char_buf
std::string str;
int char_count=0;
// Read the next line from File untill it reaches the 7.
while (std::getline(in, str)&& char_count!=7)
{
// Line contains string of length > 0 then save it in vector
if (str.size() > 0)
your_char_vector.push_back(str);
char_count++;
if(char_count==7)
break;
}
I am trying to replace arrays with vectors but I can't figure out how.
Replace this function to dynamically allocate memory for vectors:
string readFile(string filename, string** list, int size){
*list = new string[size];
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
*(*list + i) = line;
}
file.close();
return **list;
}
And here's my attempt to change it to vectors with no luck. Any feedback is greatly appreciated:
string processFile(string filename, vector<string>** list, int size){
*list = new vector<string>(size);
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
*list[i] = line; // error
}
file.close();
return **list; // error
}
You will need some proper error handling, but basically, you need neither pointers nor fixed sizes if you use containers:
std::vector<std::string> readLinesFromFile(const std::string& filename)
{
std::vector<std::string> result;
std::ifstream file(filename);
for (std::string line; std::getline(file, line); )
{
result.push_back(line);
}
return result;
}
There are several problems:
You don't need to use vector**, vector is equivalent to the list in previous code.
The return type is string, but you are returning vector**
This code should work, not tested though:
void processFile(string filename, vector<string>& list, int size){
//list = new vector<string>(size); // no need if you get a vector reference
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
list.push_back(line); //the error was because you are assigning string to a vector<string>*
}
file.close();
// you dont have to return, as vector is passed by reference
}
If you still need to use pointer of vector
void processFile(string filename, vector<string>** list, int size){
*list = new vector<string>(size); // bad practice
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
(*list)->push_back(line);
}
file.close();
// you dont have to return, as vector is passed by pointer
}
Change *list[i] = line to *list->push_back(line) and you should be okay for the first error.
The second error is going to depend on what your intent is for the return value. I think return *list->front(); will give the same result as the first example, but if you are planning on returning more than just the first line then you will need to do some concatenation. You can just create a local string and append each line as you read them.
Hopefully your teacher knows using new vector is almost always a code smell in C++ and is using this for a specific reason with a plan to fix it later.
here is a working example. enjoy :)
BTW - you don't need to pass the length, just instantiate the memory and use the push_back method.
#include <vector>
#include <fstream>
#include <string>
using namespace std;
void processFile(string filename, vector<string>** list, int size);
void main()
{
vector<string>* list = NULL;
processFile("C:\\temp.txt", &list, 13);
int i = 1;
}
void processFile(string filename, vector<string>** list, int size){
*list = new vector<string>();
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
(**list).push_back(line); // error
}
file.close();
}
When i read TestData.txt file it gives me wrong output. What am i doing wrong. I am using int array so i can do MergeSort after saving data into array.
TestData.txt
-------------------
31791 564974 477059 269094 972335
739154 206345 634644 227684 398536
910177 507975 589785 67117 395140
598829 372499 364165 450187 996527
700285 263407 918021 661467 457544
656297 846316 221731 240676 68287
913 141702 845802 477617 109824
{
int myArray[1000];
int i;
//reading givin data
const char* filename= "TestData.txt";
ifstream file(filename);
if(file.is_open())
{
for(i = 0; i <=999; ++i)
{
file >> myArray[i];//storing data to array
}
}
Need to check if you ifstream is end of file, in that case you get garbage value from out of the file bound.
With One modification, the code would be OK.
Change:
for(i = 0; i <=999; ++i)
to:
for(i = 0; i <=999 && !file.eof(); ++i)
You are reading 1000 enties from your file which contains clearly less than 1000 integers.
The first values of your array must be correct, but after you reach the end of your file the operator>> will not ready anything.
For example here is one way to write it:
const char* filename= "TestData.txt";
std::vector<int> myArray;
std::ifstream file(filename);
if(file.is_open())
{
int v;
while(file >> v) {
myArray.push_back(v);
}
}
int if I'm not wrong can keep data from -32768 to 32767.
So if u have bigger values than that (which you have, from your source file), you won't have the results you are expecting.
btw, it would be nice to know also what output you are getting.