Home > Mobile >  How to use a char array in reading from a text file?
How to use a char array in reading from a text file?

Time:10-23

The following code is working but I'm using a string instead of a char array. It is a homework question I received and I'm struggling to implement a 2D char array with pointers. Help would be much appreciated!

#include <iostream>
#include <iomanip>
#include <fstream>
    
using namespace std;

int readFromFile(string* P);
void displayData(string* P, int S);

int main()
{
    const int Size = 30;
    string arrFriends[Size];
    string* pName = arrFriends;

    int count = readFromFile(pName);

    displayData(pName, count);


    return 0;
}


int readFromFile(string* P)
{
    ifstream infile;
    infile.open("friends.txt");
    int count = 0;

    cout << "Reading from the file.";
    if(infile.fail())
    {
        cout << "\nError opening file!";
    }
    else
    {
        while(!(infile.eof()))
        {
            getline(infile, *(P   count));
            count  ;
        }
        infile.close();
    }
    cout << "\nDone!\n";
    return count;
}


void displayData(string* P, int S)
{
    cout << "\nContent of the array:\n";
    for(int i = 0; i < S; i  )
    {
        cout << *(P   i) << endl;
    }
}

2.1 Declare a char array called arrFriends that will be able to hold 30 elements. Declare a pointer for the array.

2.2 Write a method called readFromFile that will receive a pointer to the address of the first element of the array as a parameter. Read the names of a few friends from the text file called friends.txt into an array using the pointer. Return the number of elements saved in the array.

2.3 Write a function called displayData that will receive the pointer of the first element of the array and the number of elements stored in the array as parameters. Display a heading and a list of names.

2.4 In the main function, call the methods to read the name from the file and display the names from the array.

enter image description here

CodePudding user response:

If you know a fixed upper bound for the length of a name, you could simply create an array char friends[30][MaxNameLength 1].

Using a custom allocator and making sure no reallocations happen allows you to use code that's pretty similar to the code you've written.

constexpr size_t MaxFriends = 30;
constexpr size_t MaxNameLength = 255;

using Friend = char[MaxNameLength   1];

// allocator returning the storage for one friend name for use by std::basic_string
template<class T>
struct Allocator
{
    Friend* m_friend;

    Allocator(Friend* fr)
        : m_friend(fr)
    {}

    template<class U>
    Allocator(Allocator<U> const& other)
        : m_friend(other.m_friend)
    {
    }

    template<class U>
    Allocator& operator=(Allocator<U> const& other)
    {
        m_friend = other.m_friend;
        return *this;
    }
    using value_type = T;
    using size_type = size_t;

    template<class U>
    struct rebind
    {
        using other = Allocator<U>;
    };

    T* allocate(size_t n)
    {
        return reinterpret_cast<T*>(*m_friend);
    }

    void deallocate(T*, size_t)
    {
    }

    bool operator==(Allocator const& other) const
    {
        return false;
    }

    bool operator!=(Allocator const& other) const
    {
        return true;
    }

    constexpr size_t max_size() const
    {
        return sizeof(Friend) / sizeof(T);
    }

};

using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;

static_assert(sizeof(String) <= MaxNameLength, "name length may not be sufficient to pervent short string optimization");

int readFromFile(Friend* frnd)
{
    std::ifstream infile("friends.txt");
    int count = 0;

    std::cout << "Reading from the file.";
    if (infile.fail())
    {
        std::cout << "\nError opening file!";
    }
    else
    {
        while (count < MaxFriends)
        {

            String str{ Allocator<char>{ frnd   count } };
            str.reserve(MaxNameLength); // the allocator is used only once
            if (std::getline(infile, str))
            {
                  count;
            }
            else
            {
                break;
            }
        }

        infile.close();
    }
    std::cout << "\nDone!\n";
    return count;
}


void displayData(Friend* P, int S)
{
    std::cout << "\nContent of the array:\n";
    for (int i = 0; i < S; i  )
    {
        std::cout << P[i] << std::endl;
    }
}

int main() {
    Friend friends[MaxFriends]{};
    auto const friendCount = readFromFile(friends);

    displayData(friends, friendCount);
}

CodePudding user response:

Evolution.

Implementation of your homework with pointer to first character of 2d array

#include <iostream>
#include <fstream>

#define MAX_NUMBER_FRIENDS 30
#define MAX_NAME_LENGTH 50

int readFromFile(char *dataPointer)
{
    int count = 0;

    std::ifstream infile;
    infile.open("r:\\friends.txt");

    if (infile.fail())
    {
        std::cout << "\nError opening file!\n";
    }
    else
    {
        std::cout << "Reading from the file.";
        do 
        {
            infile.getline(dataPointer, MAX_NAME_LENGTH);
            if (not infile.fail()) {
                count  ;
                dataPointer  = MAX_NAME_LENGTH;
            }
        } while ((not infile.fail()) and (count < MAX_NUMBER_FRIENDS));
        infile.close();
        std::cout << "\nDone!\n";
    }
    return count;
}
void displayData(char* dataPointer, int count) {
    std::cout << "\nContent of the array\n";
    for (int i = 0; i < count;   i) {
        std::cout << dataPointer << '\n';
        dataPointer  = MAX_NAME_LENGTH;
    }
    std::cout << '\n';
}

int main() {

    char arrFriends[MAX_NUMBER_FRIENDS][MAX_NAME_LENGTH];
    char* arrFriendsPointer = &arrFriends[0][0];

    int numberOfNames = readFromFile(arrFriendsPointer);
    displayData(arrFriendsPointer, numberOfNames);
}

Next, with comments. Looks immediately better:

#include <iostream>
#include <fstream>

// Compile Time Specification: Numbers are not magic, but given by requirement
// This will define the number of the elements in the friends array
#define MAX_NUMBER_FRIENDS 30
// This is the maximum length of the name
#define MAX_NAME_LENGTH 50

// Read a a list of names from a file and store it in a given array
int readFromFile(char* dataPointer) {
    
    // We will count the number of names in the file and return this info to the caller of this function
    int nameCount = 0;

    // Open the input text file with names
    std::ifstream nameFileStream;
    nameFileStream.open("r:\\friends.txt");

    // Check, if the file could be opened and that there is no failure
    if (nameFileStream.fail()) {
        // Error. There was a failure. File could not be opened
        std::cout << "\nError opening file!\n";
    }
    else
    {
        // File could be opened. Give status message
        std::cout << "Reading from the file.";
        do
        {
            // Read one name from the file. Protect from out of bound error
            nameFileStream.getline(dataPointer, MAX_NAME_LENGTH);

            // Check, if the name could be read, or, if there was a failure
            if (not nameFileStream.fail()) {

                // Name could be successfully read. Increase name counter
                nameCount  ;
                // And point to the next row in the 2d array
                dataPointer  = MAX_NAME_LENGTH;
            }
            // Loop end condition will check for stream failure or too many names
        } while ((not nameFileStream.fail()) and (nameCount < MAX_NUMBER_FRIENDS));

        // Close the file at the end
        nameFileStream.close();

        // Final status message
        std::cout << "\nDone!\n";
    }
    return nameCount;
}

// Display the data
void displayData(char* dataPointer, int count) {

    // Give user information
    std::cout << "\nContent of the array\n";

    // In a loop, show all names
    for (int i = 0; i < count;   i) {

        // Output name
        std::cout << dataPointer << '\n';

        // Set pointer to next row in 2d array
        dataPointer  = MAX_NAME_LENGTH;
    }
    std::cout << '\n';
}

int main() {

    // Define a 2 dimensional array to hold a number of name strings
    char arrFriends[MAX_NUMBER_FRIENDS][MAX_NAME_LENGTH];

    // This is a pointer to the first character in the 2d array
    char* arrFriendsPointer = &arrFriends[0][0];

    // Read and show data
    int numberOfNames = readFromFile(arrFriendsPointer);
    displayData(arrFriendsPointer, numberOfNames);
}

Now, with a pointer to the first row of the 2d array. And some further improvements

#include <iostream>
#include <fstream>

// Compile Time Specification: Numbers are not magic, but given by requirement
// This will define the number of the elements in the friends array
constexpr unsigned int MaxNumberFriends = 30;
// This is the maximum length of the name
constexpr unsigned int MaxNameLength = 50;


// Read a a list of names from a file and store it in a given array
unsigned int readFromFile(char (*dataPointer)[MaxNameLength]) {

    // We will count the number of names in the file and return this info to the caller of this function
    unsigned int nameCount{};

    // Open the input text file with names
    std::ifstream nameFileStream{ "r:\\friends.txt"};

    // Check, if the file could be opened and that there is no failure
    if (nameFileStream.fail()) {
        // Error. There was a failure. File could not be opened
        std::cout << "\nError opening file!\n";
    }
    else {
        // File could be opened. Give status message
        std::cout << "Reading from the file.";
        do {
            // Read one name from the file. Protect from out of bound error
            nameFileStream.getline(dataPointer[nameCount], MaxNameLength);

            // Check, if the name could be read, or, if there was a failure
            if (not nameFileStream.fail()) {

                // Name could be successfully read. Increase name counter
                nameCount  ;
            }
            // Loop end condition will check for stream failure or too many names
        } while ((not nameFileStream.fail()) and (nameCount < MaxNumberFriends));

        // Final status message
        std::cout << "\nDone!\n";
    }
    return nameCount;
}

// Display the data
void displayData(char(*dataPointer)[MaxNameLength], int count) {

    // Give user information
    std::cout << "\nContent of the array\n";

    // In a loop, show all names
    for (int i = 0; i < count;   i) {

        // Output name
        std::cout << dataPointer[i] << '\n';
    }
    std::cout << '\n';
}

int main() {

    // Define a 2 dimensional array to hold a number of name strings
    char arrFriends[MaxNumberFriends][MaxNameLength]{};

    // This is a pointer to the first row in the 2d array
    char(*arrFriendsPointer)[MaxNameLength] {&arrFriends[0]};

    // Read and show data
    unsigned int numberOfNames = readFromFile(arrFriendsPointer);
    displayData(arrFriendsPointer, numberOfNames);
}

And now with a pointer to the complete 2d array. And some further improvements . . .

#include <iostream>
#include <fstream>

// Compile Time Specification: Numbers are not magic, but given by requirement
// This will define the number of the elements in the friends array
constexpr unsigned int MaxNumberFriends = 30;
// This is the maximum length of the name
constexpr unsigned int MaxNameLength = 50;


// Read a a list of names from a file and store it in a given array
unsigned int readFromFile(char (*dataPointer)[MaxNumberFriends][MaxNameLength]) {

    // We will count the number of names in the file and return this info to the caller of this function
    unsigned int nameCount{};

    // Open the input text file with names
    std::ifstream nameFileStream{ "r:\\friends.txt" };

    // Check, if the file could be opened and that there is no failure
    if (nameFileStream.fail()) {
        // Error. There was a failure. File could not be opened
        std::cout << "\nError opening file!\n";
    }
    else {
        // File could be opened. Give status message
        std::cout << "Reading from the file.";
        do {
            // Read one name from the file. Protect from out of bound error
            nameFileStream.getline((*dataPointer)[nameCount], MaxNameLength);

            // Check, if the name could be read, or, if there was a failure
            if (not nameFileStream.fail()) {

                // Name could be successfully read. Increase name counter
                nameCount  ;
            }
            // Loop end condition will check for stream failure or too many names
        } while ((not nameFileStream.fail()) and (nameCount < MaxNumberFriends));

        // Final status message
        std::cout << "\nDone!\n";
    }
    return nameCount;
}

// Display the data
void displayData(char(*dataPointer)[MaxNumberFriends][MaxNameLength], int count) {

    // Give user information
    std::cout << "\nContent of the array\n";

    // In a loop, show all names
    for (int i = 0; i < count;   i) {

        // Output name
        std::cout << (*dataPointer)[i] << '\n';
    }
    std::cout << '\n';
}

int main() {

    // Define a 2 dimensional array to hold a number of name strings
    char arrFriends[MaxNumberFriends][MaxNameLength]{};

    // This is a pointer to the 2d array
    char (* arrFriendsPointer)[MaxNumberFriends][MaxNameLength] = &arrFriends;

    //Read and show data
    unsigned int numberOfNames = readFromFile(arrFriendsPointer);
    displayData(arrFriendsPointer, numberOfNames);
}

A little bit more C

#include <iostream>
#include <fstream>

// Compile Time Specification: Numbers are not magic, but given by requirement
// This will define the number of the elements in the friends array
constexpr unsigned int MaxNumberFriends{ 30u };
// This is the maximum length of the name
constexpr unsigned int MaxNameLength{ 50u };

// Defining types
using Array2d = char[MaxNumberFriends][MaxNameLength];
using Array2dPtr = Array2d*;

// Read a a list of names from a file and store it in a given array
size_t readFromFile(Array2dPtr dataPointer) {

    // We will count the number of names in the file and return this info to the caller of this function
    size_t nameCount{};

    // Open the input text file with names and check, if it could be opened
    if (std::ifstream nameFileStream{ "r:\\friends.txt" }; nameFileStream) {

        // File could be opened. Give status message
        std::cout << "Reading from the file.";
        do {
            // Read one name from the file. Protect from out of bound error
            nameFileStream.getline((*dataPointer)[nameCount], MaxNameLength);

            // Check, if the name could be read, or, if there was a failure
            if (not nameFileStream.fail())   nameCount;
            
            // Loop end condition will check for stream failure or too many names
        } while ((not nameFileStream.fail()) and (nameCount < MaxNumberFriends));

        // Final status message
        std::cout << "\nDone!\n";
    }
    else std::cerr << "\n*** Error: File could not be opened.\n";

    return nameCount;
}

// Display the data
void displayData(Array2dPtr dataPointer, size_t count) {

    // Give user information
    std::cout << "\nContent of the array\n";

    // In a loop, show all names
    for (size_t i{}; i < count;   i)
        std::cout << (*dataPointer)[i] << '\n';
    std::cout << '\n';
}

int main() {

    // Define a 2 dimensional array to hold a number of name strings
    Array2d arrFriends{};

    // This is a pointer to the 2d array
    Array2dPtr arrFriendsPointer{ &arrFriends};

    //Read and show data
    size_t numberOfNames = readFromFile(arrFriendsPointer);
    displayData(arrFriendsPointer, numberOfNames);
}

And, at the end, one of many full blown C solutions:

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

struct Names {
    std::vector<std::string> names{};

    size_t count() const { return names.size(); }
    // Define simple extractor
    friend std::istream& operator >> (std::istream& is, Names& n) {
        n.names.clear();
        for (std::string line{}; std::getline(is, line); n.names.push_back(line));
        return is;
    }
    // Define simple inserter
    friend std::ostream& operator << (std::ostream& os, const Names& n) {
        std::copy(n.names.begin(), n.names.end(), std::ostream_iterator< std::string>(os, "\n"));
        return os;
    }
};
size_t readFromFile(Names& names) {

    // Open the input text file with names and check, if it could be opened
    if (std::ifstream nameFileStream{ "r:\\friends.txt" }; nameFileStream) 

        // Rad everything with one simple statement
        nameFileStream >> names;

    else std::cerr << "\n*** Error: File could not be opened.\n";

    // Return number of elements
    return names.count();
}

int main() {

    Names names{};

    size_t numberOfNames = readFromFile(names);

    std::cout << "\nNumber of names read: " << numberOfNames << "\n\n" << names << '\n';
}

Have fun . . .

  • Related