2

I cant write a words from a file to an array.

I have tried to use char and strings, but i have problem with both of them.

FILE *file = fopen("films.txt", "r");
string FILMS[500];
while (!feof(file))
{
    fscanf(file, "%s", FILMS);
    //fgets(FILMS, 500, file);
}

I expect that in each cell there will be a word.

Roomey
  • 716
  • 2
  • 9
  • 16

3 Answers3

1

Use the C++ classes and functions to make it easier. Instead of a fixed C style array of exactly 500 films, use a std::vector<std::string>> that will grow dynamically when you put film titles in it.

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

std::vector<std::string> get_films() {
    std::ifstream file("films.txt");
    std::vector<std::string> FILMS;
    if(file) { // check that the file was opened ok
        std::string line;
        // read until getline returns file in a failed/eof state
        while(std::getline(file, line)) {
            // move line into the FILMS vector
            FILMS.emplace_back(std::move(line));
            // make sure line is in a specified state again
            line.clear();
        }
    }
    return FILMS;
} // an fstream is automatically closed when it goes out of scope

int main() {
    auto FILMS = get_films();
    std::cout << "Read " << FILMS.size() << " film titles\n";
    for(const std::string& film : FILMS) {
        std::cout << film << "\n";
    }
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • if use ```std::string``` and ```std::vector``` then might as well use ```std::copy``` – Andrew Kashpur Aug 02 '19 at 09:28
  • Actually simply `push_back` (copy) instead of `move` would be more efficient in this case. You want to keep re-using `line` as a buffer to avoid costly reallocations. – rustyx Aug 02 '19 at 10:12
  • I like both proposals. A `std::copy` is nice and using copy may be more efficient. I haven't compared the two actuallly ... Can we get figures on just how much more effficient that'd be? – Ted Lyngmo Aug 04 '19 at 03:22
0

As I'm not sure why you tried using c style arrays and files, I posted a 'not too elegant' solution like that one, too, hoping it might help. You could always try to make it more dynamic with some malloc (or new), but I sticked with the easy solution for now.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

void readcpp(const char* fname, std::vector<std::string>& data)
{
    std::ifstream file_in(fname, std::ios::in);

    if (file_in.is_open())
    {
        std::string film;

        while (std::getline(file_in, film))
        {
            data.push_back(film);
        }

        file_in.close();
    }
    else std::cerr << "file cant be opened" << std::endl;
}

#include <cstdio>
#include <cstdlib>
#include <cstring>

void readc(const char* fname, char data[500][500])
{
    FILE* file_in = fopen(fname, "r");

    if (file_in)
    {
        char film[500];

        for (unsigned int i = 0; fgets(film, 500, file_in) && i < 500; i++)
        {
            memcpy(data + i, film, 500);
        }

        fclose(file_in);
    }
    else fprintf(stderr, "file cant be opened\n");
}

int main()
{
    const char* fname = "films.txt";
    char cFilms[500][500];
    std::vector<std::string> cppFilms;

    readc(fname, cFilms);
    readcpp(fname, cppFilms);

    return 0;
}

And as the others mentioned before, do not use feof or for that matter, ifstream's eof member function either, for checking wheter you reached the end of file, as it may be unsafe.

skyzip
  • 237
  • 2
  • 11
0

Hm, I see a lot of code in answers.

The usage of algorithm will drastically reduce coding effort.

Additionally it is a "more modern" C++ approach.

The OP said, that he want to have words in some array. OK.

So we will use a std::vector<std::string> for storing those words. As you can see in cppreference, the std::vector has many different constructors. We will use number 4, the range constructor.

This will construct the vector with a range of similar data. The similar data in our case are words or std::string. And we would like to read the complete range of the file, beginning with the first word and ending with the last word in the file.

For iterating over ranges, we use iterators. And for iterating of data in files, we use the std::istream_iterator. We tell this function what we want to read as template parameter, in our case a std::string. Then we tell it, from which file to read.

Since we do not have files on SO, I use a std::istringstream. But that's the same reading from a std::ifstream. If you have na open file stream, then you can hand it over to the std::istream_iterator.

And the result of using this C++ algorithms is that we read the complete file into the vector by just defining the varaible with its constructer as a one-liner.

We do similar for the debug output.

#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <sstream>

std::istringstream filmFile{ R"(Film1 Film2
   Film3  Film4 Film5
Film6
)" };

int main()
{
    // Define the variable films and use its range constructor
    std::vector<std::string> films{ std::istream_iterator<std::string>(filmFile), std::istream_iterator<std::string>() };

    // For debug pruposes, show result on console
    std::copy(films.begin(), films.end(), std::ostream_iterator<std::string>(std::cout, "\n"));

    return 0;
}

A M
  • 14,694
  • 5
  • 19
  • 44