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.
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 . . .