0

So I am trying to read string data from a file into a dynamically allocated array, but I cannot seem to get the correct code for doing so. I have below a code using an array of a preset size but that would not be efficient, hence why I want to use dynamic memory allocation. I know I have to use pointers but I am fairly new to the concept so any help would be greatly appreciated.

#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <string>

#define SIZE 100

using namespace std;

void loadData();

int main()
{
    loadData();
    return 0;
    {

        string fileName;
        std::string wordArray[SIZE];
        cout << "Please enter the name of the text file you want to process followed by '.txt': " << endl;
        cin >> fileName;

        ifstream dataFile(fileName);
        if (dataFile.fail()) {
            cerr << fileName << " could not be opened." << endl; //error message if file opening fails
            exit(-1);
        }
        while (!dataFile.eof()) {
            for (int i = 0; i < SIZE; i++) {
                dataFile >> wordArray[I];
                for (std::string& s : wordArray) //this for loop transforms all the words in the text file into lowercase
                    std::transform(s.begin(), s.end(), s.begin(),
                        [](unsigned char c) { return std::tolower(c); });
            }
        }
        dataFile.close();
    }
}
5
  • 3
    What about a std::vector<std::string> ? Commented Nov 1, 2021 at 13:50
  • There have to be hundreds of examples in the internet. Is there something you have tried already? Commented Nov 1, 2021 at 13:50
  • 1
    Mandatory read: Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? Commented Nov 1, 2021 at 13:52
  • 1
    return 0; on the second line of main() will cause the program to end. No sure why you put that there. I am also not sure what loadData() actually accomplishes since it does not have any parameters. Commented Nov 1, 2021 at 13:53
  • 1
    I know I have to use pointers -- No, you don't have to use pointers. A std::vector<std::string> relieves you of having to use pointers. Commented Nov 1, 2021 at 13:54

2 Answers 2

1

The below program show how you can store the strings read from the input.txt file and store them in lowercase in a std::vector.

Version 1: Stores word by word(in lowercase) into the vector


#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>
int main()
{
    std::ifstream inputFile("input.txt");
    
    //create vector that will contain the words in the file in lowercase 
    std::vector<std::string> wordVec;
    
    std::string line, individualWord;
    
    if(inputFile)
    {
        while(std::getline(inputFile, line, '\n'))
        {
            std::istringstream ss(line);
            while(ss >> individualWord)//word by word
            {
                std::transform(individualWord.begin(), individualWord.end(), individualWord.begin(),
                [](unsigned char c)
                { return std::tolower(c); 
                    
                });
                
                wordVec.push_back(individualWord);
            }
            
        
            
        }
    }
    else 
    {
        std::cout<<"file could not be opened"<<std::endl;
    }
    inputFile.close();
    
    //lets print out the elements of the vector to check if elements are correctly stored in lowercase 
    for(const std::string &elem: wordVec)
    {
        std::cout<<elem<<std::endl;
    }
    return 0;
}

The output of version 1 can be seen here.

Version 2: Stores a complete single line(in lowercase) into the vector

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
int main()
{
    std::ifstream inputFile("input.txt");
    
    //create vector that will contain the words in the file in lowercase 
    std::vector<std::string> wordVec;
    
    std::string line;
    
    if(inputFile)
    {
        while(std::getline(inputFile, line, '\n'))
        {
            std::transform(line.begin(), line.end(), line.begin(),
            [](unsigned char c)
            { return std::tolower(c); 
                
            });
            
            wordVec.push_back(line);
        
            
        }
    }
    else 
    {
        std::cout<<"file could not be opened"<<std::endl;
    }
    inputFile.close();
    
    //lets print out the elements of the vector to check if elements are correctly stored in lowercase 
    for(const std::string &elem: wordVec)
    {
        std::cout<<elem<<std::endl;
    }
    return 0;
}

The output of the above(version 2) program can be seen here.

The difference between version 1 and 2 is that version 1 reads a complete line and then read word by word and store those words(in lowercase) into the std::vector while version 2 reads a complete line(which ends with '\n') and store that line(in lowercase) into the std::vector.

Sign up to request clarification or add additional context in comments.

1 Comment

I am very sorry that I do not meet your expectations. I will try to improve myself and try to become more skilled in C++. Thank you for giving the hint that 3 or 4 statements for solving the problem maybe too complicated. I added an additional solution that only needs 2 statements. Thank you again.
1

Sometime life could be easy. By using modern C++ elements, the implementation will be really simple in the end.

I am not so sure what I should explain for 3 lines of code. It is basically visible with the comments in the code.

Please see the first solution:

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

int main() {

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

        // Define a vector and read all words from the file
        std::vector words(std::istream_iterator<std::string>(inputStream), {});

        // Show result to the user
        std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    }
}

Then next solution converts the word into lower case. So, I had to write 4 statements. Please see the below second solution.

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

int main() {

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

        std::vector<std::string> words{};

        // Read all words from the file and convert to lower case
        std::transform(std::istream_iterator<std::string>(inputStream), {}, std::back_inserter(words), 
            [](std::string w) { for (char& c : w) c = std::tolower(c); return w; });

        // Show result to the user
        std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    }
}

If data does not need to be stored, we can come up with a 2-statement version.

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>

int main() {

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

        // Read all words from the file and convert to lower case and output it
        std::transform(std::istream_iterator<std::string>(inputStream), {}, std::ostream_iterator<std::string>(std::cout, "\n"),
            [](std::string w) { for (char& c : w) c = std::tolower(c); return w; });
    }
}

Sometime teachers want the students to learn dynamic memory management using pointers.

But the usage of pointers for owned memory is strongly discouraged. The std::vector has been invented for that reason over a decade ago.

Anyway, I will also show a solution using new. It works, but you should not use it.

int main() {

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

        // Do some initial allocation of memory
        std::string* words = new std::string[1]{};
        unsigned int numberOfAvaliableSlotsInDynamicArray{1};

        // Now we want to read words. We want also to count the words,so that we can allocate appropriate memory
        std::string word{};
        unsigned int wordCounter{};

        // Read all words in a loop
        while (inputStream >> word) {

            // Check, if we still have enough space in our dynamic array
            if (wordCounter >= numberOfAvaliableSlotsInDynamicArray) {

                // Oh, we are running out of space. Get more memory
                numberOfAvaliableSlotsInDynamicArray *= 2;
                std::string* temp = new std::string[numberOfAvaliableSlotsInDynamicArray]{};

                // Copy all existing data into new array
                for (unsigned int i{}; i < wordCounter; ++i)
                    temp[i] = words[i];

                // Delete old memory
                delete[] words;

                // And assign new storage to words
                words = temp;
            }
            // STore the recently read word at the end of the array
            words[wordCounter] = word;

            // Count words. Now we have one word more
            ++wordCounter;
        }
        // Now we have read all words from the file. Show output
        for (unsigned int i{}; i < wordCounter; ++i)
            std::cout << words[i] << '\n';

        // Release memory
        delete[] words;
    }
}

And even smart pointers, which shall be used as pointers if at all, are not nice.

Also the following should not be used.

#include <iostream>
#include <fstream>
#include <string>
#include <memory>

int main() {

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

        // Do some initial allocation of memory
        std::unique_ptr<std::string[]> words = std::unique_ptr<std::string[]>(new std::string[1]);
        unsigned int numberOfAvaliableSlotsInDynamicArray{ 1 };

        // Now we want to read words. We want also to count the words,so that we can allocate appropriate memory
        std::string word{};
        unsigned int wordCounter{};

        // Read all words in a loop
        while (inputStream >> word) {

            // Check, if we still have enough space in our dynamic array
            if (wordCounter >= numberOfAvaliableSlotsInDynamicArray) {

                // Oh, we are running out of space. Get more memory
                numberOfAvaliableSlotsInDynamicArray *= 2;

                std::unique_ptr<std::string[]> temp = std::unique_ptr<std::string[]>(new std::string[numberOfAvaliableSlotsInDynamicArray]);

                // Copy all existing data into new array
                for (unsigned int i{}; i < wordCounter; ++i)
                    temp[i] = std::move(words[i]);

                // And assign new storage to words
                words = std::move(temp);
            }
            // STore the recently read word at the end of the array
            words[wordCounter] = word;

            // Count words. Now we have one word more
            ++wordCounter;
        }
        // Now we have read all words from the file. Show output
        for (unsigned int i{}; i < wordCounter; ++i)
            std::cout << words[i] << '\n';
    }
}

Conclusion: Use a std::vector.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.