【问题标题】:how to read data from text file and store it into array in c++如何从文本文件中读取数据并将其存储到C++中的数组中
【发布时间】:2021-07-10 01:45:33
【问题描述】:

我正在尝试从文本文件中读取数据并将其存储到数组中。我需要它来解决 FEM 问题。假设我的文本文件如下:

node: 1,2,3,4,5,6,7,8,9,10
x: 4,4,3.75,3.76773151,3,3.59192947,4,3.5,3.55115372,3.375, 3.71330586 
y: 3,275,3,2.65921885,2.79192947,2.5,3,2.55115372,2.78349365,2.36222989 
z: 0,0,0,0,0,0,0,0,0,0                      

我想将此数据从文本文件存储到 10*4 矩阵 (myarray[10][4]) 中。我还需要将此数组的每一列存储到一个向量中。假设我的向量是:

double x[10];
double y[10];
double z[10];

for (int i = 0; i < 10; i++)
{
    x[i] = myarray[i][1];
    y[i] = myarray[i][2];
    z[i] = myarray[i][3];
}

我这样写代码:

int main()
{
    string line;
    string coordinate[10][4];
    ifstream mesh("mesh.txt");

    for (int i = 0; i < 10; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            if (getline(mesh, line, ';'))
            {
                coordinate[i][j] = line;
                cout << coordinate[i][j] << endl;
                cout << "mesh : " << line[0] << endl;
            }
        }
    }
    mesh.close();
}

现在我的问题是,当我想将每一列坐标放入一个向量时,我得到了这个错误:

不存在从字符串到双精度的合适转换函数

我不明白这个错误,需要帮助解决它。

【问题讨论】:

  • 提示:如果可以,请使用 std::vector 而不是 C 数组。 push_back 省去了很多麻烦。您还想使用 std::tuple 之类的东西来创建您的 x/y/z 坐标,或者甚至可以制作一个简单的 struct point { double x,y,z; }
  • 提示:x[i]=myarray[i][1] 不会发生。您必须convert your string to a double,如x[i] = std::stod(myarray[i][1])
  • 提醒:C++ 数组是零索引的。您正在跳过索引 0,并使用 1-3。
  • 您好,感谢您的回答。您能否在代码中告诉我应该更改哪一部分?
  • 为什么 x[i]=myarray[i][1] 不会发生?我是 C++ 新手,我不能将矩阵的一列放入向量中?

标签: c++ arrays string double


【解决方案1】:

一种迭代方式可能像这样,在自定义的迭代器类中使用拆分器。它似乎很复杂,但我认为它很容易维护。 请注意,迭代器类是这个伟大的answer 的修改版本。

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

template <typename T>
class istream_line_iterator : public std::iterator<std::input_iterator_tag, T> {
  std::istream* stream;

 public:
  // Creating from a stream or the end iterator.
  istream_line_iterator(std::istream& s) : stream(&s) { dropLeadingSpace(); }
  istream_line_iterator() : stream(nullptr) {}

  // Copy
  istream_line_iterator(istream_line_iterator const& copy)
      : stream(copy.stream) {}
  istream_line_iterator& operator=(istream_line_iterator const& copy) {
    stream = copy.stream;
    return *this;
  }

  // The only valid comparison is against the end() iterator.
  // All other iterator comparisons return false.
  bool operator==(istream_line_iterator const& rhs) const {
    return stream == nullptr && rhs.stream == nullptr;
  }
  bool operator!=(istream_line_iterator const& rhs) const {
    return !(*this == rhs);
  }

  // Geting the value modifies the stream and returns the value.
  // Note: Reading from the end() iterator is undefined behavior.
  T operator*() const {
    T value;
    (*stream) >> value;
    return value;
  }
  T* operator->() const;  // Not sure I want to implement this.

  // Input streams are funny.
  // Does not matter if you do a pre or post increment. The underlying stream
  // has changed. So the effect is the same.
  istream_line_iterator& operator++() {
    dropLeadingSpace();
    return *this;
  }
  istream_line_iterator& operator++(int) {
    dropLeadingSpace();
    return *this;
  }

 private:
  void dropLeadingSpace() {
    // Only called from constructor and ++ operator.
    // Note calling this on end iterator is undefined behavior.

    char c;
    while ((*stream) >> std::noskipws >> c) {
      if (c == '\n') {
        // End of line. So mark the iterator as reaching end.
        stream = nullptr;
        return;
      }
      if (!std::isspace(c) && c != ',') {
        // Found a non space character so put it back
        stream->putback(c);
        return;
      }
    }
    // End of stream. Mark the iterator as reaching the end.
    stream = nullptr;
  }
};

int main() {
  std::ifstream ifs("1.in");
  std::string line;
  std::vector<std::vector<double>> vec;
  while (std::getline(ifs, line)) {
    std::istringstream iss(line);
    std::string pre;
    iss >> pre;

    std::vector<double> line_vec;
    auto beg = istream_line_iterator<double>(iss);
    auto end = istream_line_iterator<double>();

    std::copy(beg, end, std::back_inserter(line_vec));
    vec.push_back(std::move(line_vec));
  }
  for (const auto& inner : vec) {
    for (auto d : inner) {
      std::cout << d << ' ';
    }
    std::cout << std::endl;
  }

  return 0;
}

【讨论】:

  • 我不会认为编写自定义迭代器是“快速而肮脏的方式”。我只会阅读每一行,然后使用 std::istringstreamstd::getline 来读取逗号分隔的值。
  • @paddy 很快,因为我在 SO 中找到了可重用的代码 :)
  • 这不是又快又脏,实际上它过于复杂。
  • 我会称之为缓慢和肮脏。
猜你喜欢
  • 2020-12-26
  • 1970-01-01
  • 2020-02-09
  • 2015-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多