【问题标题】:Read 2d array from txt file in c++ [duplicate]从C++中的txt文件读取二维数组[重复]
【发布时间】:2022-01-16 15:56:21
【问题描述】:

这是我的代码

#include<bits/stdc++.h>
using namespace std;

int main()
{
    char arr1[10][10];
    cout << "Reading Start" << endl;
    ifstream rfile("test.txt");
    rfile.getline(arr1[10], 10);
    int i, j;
    for (i = 0; i < 6; i++)
    {
        for (j = 0; i < 6; j++)
        {
            cout << arr1[i][j];
        }
    }
    cout << "\nRead Done" << endl << endl;
    rfile.close();
}

这是我的 test.txt 文件

0 4 7 0 0 0
4 0 0 5 3 0
7 0 0 0 6 0
0 5 3 0 0 2
0 3 4 0 0 2
0 0 0 2 2 0

我想阅读这个矩阵,但是当使用上面的代码时,它会显示核心转储输出,谁能给我一个更好的解决方案来做这件事?

【问题讨论】:

标签: c++ arrays matrix multidimensional-array file-handling


【解决方案1】:

谁能给我一个更好的解决方案来做这件事?

更好的替代方法是使用 2D vector,如下所示。在数组上使用vector优势是您不需要事先指定(知道)行和列。也就是说,文本输入文件可以有尽可能多的行和列,并且无需询问用户(或预分配)文件有多少行和列。 std::vector照顾它,如下所示。

以下程序使用 2D std::vector 以 2D 方式存储信息(如本例中的整数值)。从文件中读取所有值后,您可以根据需要处理向量。所示程序从 input.txt 读取数据(int 值)并将其存储在 2D vector 中。此外,该程序即使列数不均匀也能正常工作。您可以使用以下程序作为参考(起点)。

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include<fstream>
int main() {
    std::string line;
    int word;
    
    std::ifstream inFile("input.txt");
    
    //create/use a std::vector instead of builit in array 
    std::vector<std::vector<int>> vec;
    
    if(inFile)
    {
        while(getline(inFile, line, '\n'))        
        {
            //create a temporary vector that will contain all the columns
            std::vector<int> tempVec;
            
            std::istringstream ss(line);
            
            //read word by word(or int by int) 
            while(ss >> word)
            {
                //std::cout<<"word:"<<word<<std::endl;
                //add the word to the temporary vector 
                tempVec.push_back(word);
            }      
            //now all the words from the current line has been added to the temporary vector 
            vec.emplace_back(tempVec);
        }    
    }
    else 
    {
        std::cout<<"file cannot be opened"<<std::endl;
    }
    
    inFile.close();
    //now you can do the whatever processing you want on the vector

    //lets check out the elements of the 2D vector so the we can confirm if it contains all the right elements(rows and columns)
    for(std::vector<int> &newvec: vec)
    {
        for(const int &elem: newvec)
        {
            std::cout<<elem<<" ";
        }
        std::cout<<std::endl;
    }
    return 0;
}

上面程序的输出可以看到here。上面提到的链接也给出了读取 int 值的输入文件。

使用矢量的优点

  1. 您无需询问用户输入文件中的行数和列数。也就是说,您不必修复(硬编码)数组的大小。

  2. 即使任何特定行中的条目不均匀,上述程序也能正常工作。

  3. std::vector 为您处理内存管理。所以你不必自己使用newdelete,这需要更多的关注/关心。(如果你想在heap上创建数组)

【讨论】:

  • 这无疑是一个很好的解决方案,但是用向量来做是完全不同的事情。反正很好的解决方案
  • @LalitKumar 你说的完全不同是什么意思? OP 要求提供更好的解决方案,Anoop 提供了它。此外,std::vector 比使用内置数组更适合 OP 的目的(OP 正在寻找什么)。显然我同意你的看法,这是一个很好的解决方案。
  • @JasonLiam 与数组相比,向量的性能通常不同。
【解决方案2】:

因为有很多可能的解决方案,让我们只展示其中的一些。

基本区别是:

  • 如果我们在编译时知道数组的维度,那么,如果有编译时常量,那么我们仍然可以使用 C 样式数组,或者更好的是 std::array
  • 如果我们不知道源数据数组的维度,那么我们需要一个可以增长的动态容器,例如std::vector

在所有情况下,我们都可以将索引运算符 [] 与标准 for 循环或基于范围的带有引用的 for 循环一起使用。没有区别。


例子:

具有标准 for 循环和基于索引的访问的 C 样式数组

#include <iostream>
#include <fstream>

constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;

int main() {

    // Open the sourcefile
    std::ifstream sourceFileStream{ "test.txt" };

    // And check, if it could be opened
    if (sourceFileStream) {

        // Define 2D array to hold all data and initialize it with all 0
        char array2D[NumberOfRows][NumberOfColumns]{};

        // Read the rows and columns from the source file
        for (int row = 0; row < NumberOfRows; ++row)
            for (int col = 0; col < NumberOfColumns; ++col)
                sourceFileStream >> array2D[row][col];

        // Debug output
        for (int row = 0; row < NumberOfRows; ++row) {
            for (int col = 0; col < NumberOfColumns; ++col)  std::cout << array2D[row][col] << ' ';
            std::cout << '\n';
        }
    }
    else std::cerr << "\nError: Could not open source file\n\n";
}

具有基于范围的 for 循环和引用访问的 C 样式数组

#include <iostream>
#include <fstream>

constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;

int main() {

    // Open the sourcefile
    std::ifstream sourceFileStream{ "test.txt" };

    // And check, if it could be opened
    if (sourceFileStream) {

        // Define 2D array to hold all data and initialize it with all 0
        char array2D[NumberOfRows][NumberOfColumns]{};

        // Read the rows and columns from the source file
        for (auto& row : array2D)
            for (auto& col : row)
                sourceFileStream >> col;

        // Debug output
        for (const auto& row : array2D) {
            for (const auto& col : row) std::cout << col << ' ';
            std::cout << '\n';
        }
    }
    else std::cerr << "\nError: Could not open source file\n\n";
}

C++ std::array 带有基于范围的 for 循环

#include <iostream>
#include <fstream>
#include <array>

constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;

int main() {

    // Open the sourcefile
    std::ifstream sourceFileStream{ "test.txt" };

    // And check, if it could be opened
    if (sourceFileStream) {

        // Define 2D array toholdall data and initialize it with all 0
        std::array<std::array<char, NumberOfColumns>, NumberOfRows> array2D{};

        // Read the rows and columns from the source file
        for (auto& row : array2D)
            for (auto& col : row)
                sourceFileStream >> col;

        // Debug output
        for (const auto& row : array2D) {
            for (const auto& col : row) std::cout << col << ' ';
            std::cout << '\n';
        }
    }
    else std::cerr << "\nError: Could not open source file\n\n";
}

动态解决方案,带有std::vector

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

int main() {

    // Open the sourcefile
    std::ifstream sourceFileStream{ "test.txt" };

    // And check, if it could be opened
    if (sourceFileStream) {

        // Define 2D array to hold all data and initialize it with all 0
        std::vector<std::vector<char>> array2D{};

        // Read the rows and columns from the source file
        std::string line{};
        while (std::getline(sourceFileStream, line)) {

            // Add a new row to our matrix
            array2D.push_back(std::vector<char>{});

            // Read all column data
            char c{};
            for (std::istringstream iss(line); iss >> c; array2D.back().push_back(c))
                ;
        }
        // Debug output
        for (const auto& row : array2D) {
            for (const auto& col : row) std::cout << col << ' ';
            std::cout << '\n';
        }
    }
    else std::cerr << "\nError: Could not open source file\n\n";
}

更现代、更紧凑的 C++ 解决方案

#include <vector>
#include <string>
#include <iterator>

int main() {

    // Open the sourcefile and check, if it could be opened
    if (std::ifstream sourceFileStream{ "test.txt"}; sourceFileStream) {
    
        // Define 2D array to hold all data and initialize it with all 0
        std::vector<std::vector<char>> array2D{};

        // Read the rows and columns from the source file
        for (std::string line{}; std::getline(sourceFileStream, line);) {

            std::istringstream iss(line);
            array2D.push_back({ std::istream_iterator<char>(iss), {} });
        }


        // Debug output
        for (const auto& row : array2D) {
            for (const auto& col : row) std::cout << col << ' ';
            std::cout << '\n';
        }
    }
    else std::cerr << "\nError: Could not open source file\n\n";
}

现在,我们假设我们没有任何vector,甚至没有string

为此,我们构建了一个小类“DynamicArray”,其中包含一些函数和一个迭代器。这很容易扩展。

结果将是,在 main 中,只有一个小语句,sourceFileStream &gt;&gt; dada; 会将所有数据读入一个二维数组。

请注意。我们只对流 io 使用流函数,仅此而已。

酷。 . .

#include <iostream>
#include <sstream>
#include <fstream>

// The Dynamic Array has an initial capacity. 
// If more elements will be added, there will be a reallocation with doublecapacity
constexpr unsigned int InitialCapacity{ 4 };

// Definition of simple dynamic array class
template <typename T>
class DynamicArray {

protected:
    // Internal data ------------------------------------------------------------------------------
    T* data{};                                 // Dynamic Storage for Data
    unsigned int numberOfElements{};           // Number oe elements currently in the container
    unsigned int capacity{ InitialCapacity };  // Current maximum capacity of the container

public:
    // Construction and Destruction ---------------------------------------------------------------
    DynamicArray() { data = new T[capacity]; } // Default constructor. Allocate new memory

    DynamicArray(const DynamicArray& other) {  // Copy constructor. Make a deep copy
        capacity = numberOfElements = other.numberOfElements;
        data = new T[capacity];                // Get memory, same size as other container
        for (size_t k = 0; k < other.numberOfElements; ++k)
            data[k] = other.data[k];          // Copy data
    }
    ~DynamicArray() { delete[] data; }         // Destructor: Release previously allocated memory

    bool empty() { return numberOfElements == 0; }

    void clear() { numberOfElements = 0; };    // Clear will not delete anything. Just set element count to 0

    void push_back(const T& d) {               // Add a new element at the end
        if (numberOfElements >= capacity) {    // Check, if capacity of this dynamic array is big enough
            capacity *= 2;                     // Obviously not, we will double the capacity
            T* temp = new T[capacity];         // Allocate new and more memory
            for (unsigned int k = 0; k < numberOfElements; ++k)
                temp[k] = data[k];             // Copy data from old memory to new memory
            delete[] data;                     // Release old memory
            data = temp;                       // And assign newly allocated memory to old pointer
        }
        data[numberOfElements++] = d;          // And finally, stor the given fata at the end of the container
    }

    // Add iterator properties to class ---------------------------------------------------------------
    // Local class for iterator
    class iterator{
        T* iter{};                             // This will be the iterator 
    public:                                    // Define alias names necessary for the iterator functionality
        using iterator_category = std::input_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;

        explicit iterator(T* i) : iter(i) {};  // Default constructor for the iterator
        T operator *() const { return *iter; } // Dereferencing
        iterator& operator ++() { ++iter; return *this; } // Pre-Increment
        bool operator != (const iterator& other) { return iter != other.iter; }  // Comparison
    };

    // Begin and end function to initiliaze an iterator
    iterator begin() const { return iterator(data); }
    iterator end() const { return iterator (data + numberOfElements); }

    // Operators for class------------------------ ---------------------------------------------------------------

    T& operator[] (const size_t i) { return data[i]; }           // Index operator, get data at given index. No boundary chek

    DynamicArray& operator=(const DynamicArray& other) {         // Assignment operator. Make a deep copy
        if (this != &other) {                                    // Prevent self-assignment
            delete[] data;                                       // Release any previosly existing memory
            capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
            data = new int[capacity];                            // Get new memory, depending on size of other 
            for (unsigned int k = 0; k < numberOfElements; ++k)  // Copy other data
                data[k] = other.data[k];
        }
        return *this;
    }

    // Extractor and Inserter ------------------------ ---------------------------------------------------------------
    friend std::istream& operator >> (std::istream& is, DynamicArray& d) {
        std::stringstream ss{};
        for (char c{}; (is.get(c) and c != '\n'); ss << c);      // Read one line until newline into a stringstream
        for (T x{}; ss >> x; d.push_back(x));                    // Now extract the data from there
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const DynamicArray& d) {
        for (unsigned int k = 0; k < d.numberOfElements; ++k)    // Ultra simple output
            os << d.data[k] << ' ';
        return os;
    }
};

// -----------------------------------------------------------------------------------------------------------
// Very simple 2d array. Derived from standard dynamic array and just defining differen input and output
template <typename T>
class Dynamic2dArray : public DynamicArray<DynamicArray<T>> {

public:
    friend std::istream& operator >> (std::istream& is, Dynamic2dArray& d) {
        for (DynamicArray<T> temp{}; is >> temp; d.push_back(temp), temp.clear());
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Dynamic2dArray& d) {
        for (unsigned int k = 0; k < d.numberOfElements; ++k) 
            os << d.data[k] << '\n';
        return os;
    }
};

// -----------------------------------------------------------------------------------------------------------
int main() {

    // Open the sourcefile and check, if it could be opened
    if (std::ifstream sourceFileStream{ "test.txt" }; sourceFileStream) {

        // Define 2D array to hold all data and initialize it with all 0
        Dynamic2dArray<int> dada;

        // Read complete matrix from file
        sourceFileStream >> dada;

        // Debug output. Show complete Matrix
        std::cout << dada;
    }
    else std::cerr << "\n\nError. Could not open source file\n\n";
}

基本上,一切都是一样的,不知何故。 . .

【讨论】:

    【解决方案3】:

    还有很多其他方法可以执行特定任务,但我猜你的方法没有错,而且你只是在第二个 for 循环条件中犯了一个简单的输入错误。所以我只是为你修复你的代码。 你也可以一次只输入一个值。

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        char arr1[10][10];
        cout <<"Reading Start" <<endl;
        ifstream rfile("test.txt");
        int i,j;
        for(i=0;i<6;i++){
            for(j=0;j<6;j++){
                rfile >> arr1[i][j];
                cout << arr1[i][j] << " ";
            }
            cout << endl;
        }
    
        cout <<"\nRead Done" <<endl<<endl;
        rfile.close();
    }
    

    输出:

    【讨论】:

    • 这不是 OP 正在寻找的更好的解决方案。在您的程序中,您对数组的大小进行了硬编码,这在任何实际/实际程序中都是不可取的。
    • 但我认为如果您查看数组的属性,@JasonLiam 您永远无法在不知道其大小的情况下执行矩阵。因为他用数组标记了他的问题,所以我不想使用向量。抱歉,我知道向量很好,但他标记了数组。
    • @JasonLiam link
    • 请注意,OP 要求提供更好的解决方案。 OP 可能不知道vector。所以他/她无法标记vector
    猜你喜欢
    • 2016-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    相关资源
    最近更新 更多