输入两个不专门化大小的矩阵

Input two matrices which didn't specialize size

本文关键字:专门化 两个 输入      更新时间:2023-10-16

我需要输入两个大小不固定的矩阵,使用一个空行来声明输入每个矩阵的结束。

例如,输入:

1 2
3 4
(blank row here, end of input matrix 1)
5 6 7
8 9 10
(blank row here, end of input matrix 2)

将得到2*2矩阵和2*3矩阵。

我目前的想法是构建一个足够大的矩阵(比如1000*1000),然后设置循环并使用cin输入每个元素(代码只显示我如何输入矩阵1):

int matx1[1000][1000];
for (i = 0;i < 1000;i++)
{
for (j = 0;j < 1000;j++)
{
temp = getchar();
if (temp == 'n')
{
mat1.col = j;
break;
}
else
{
putchar(temp);
}
cin>>matx1[i][j];
}
temp = getchar();
if (temp == 'n')
{
mat1.row = i;
break;
}
else
{
putchar(temp);
}
}

当我在Xcode上运行这个程序时,会发生错误,每次按下Enter时,putchar()功能都会通过打印一个数字来中断我在终端中的输入,输入结果是混乱的。

我还尝试了以下代码以避免使用putchar():

for (i = 0; i < 1000; i++)
{
temp = getchar();
if (temp == 'n')
{
break;
}
else
{
matx1[i][0] = temp;
for (j = 1; j < 1000; j++)
{
cin >> matx1[i][j];
if (getchar() == 'n')
{
break;
}
}
}
}

尽管如此,仍然存在严重的问题。temp变量存储char,即使我使用ASCII将其转换为int,它也只能在每行的第一个元素小于10时工作,否则每行第一个元素的数据将被错误存储。

因此,主要问题是:

如何在按一次Enter后切换到新行输入相同的矩阵,并在再次按Enter后切换到输入下一个矩阵?

或者说:如何在不干扰原始输入流的情况下获得'n'的事件?

要解决手头的问题,或多或少有一种标准的方法。您想要读取csv数据。

在您的情况下,这有点困难,因为您的csv数据中确实有一种特殊的格式。因此,首先是一个"分隔的列表,然后是两个条目之间的空行。

现在,怎么能做到这一点呢?C++是一种面向对象的语言,有许多现有的算法。您可以创建、定义代理类并覆盖提取器操作符。代理类,特别是提取器,将完成所有的工作。

正如所说,提取器,这是问题的核心,有点棘手。如何做到这一点?

在提取器中,我们将首先使用函数std::getlinestd::istream读取完整的行。在行之后,我们看到一个std::string,其中包含由空格分隔的"数据字段"。std::string需要拆分,并存储"数据字段"内容。

分割字符串的过程也称为标记化。"数据字段"内容也称为"令牌"。C++有一个用于此目的的标准函数:std::sregex_token_iterator

因为我们有专门为这个目的设计的东西,我们应该使用它

这是一个迭代器。对于在字符串上迭代,因此为sregex。开始部分定义了我们应该操作的输入范围,然后有一个std::regex来表示输入字符串中应该匹配/不应该匹配的内容。匹配策略的类型由最后一个参数给出。

1 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.

我们可以使用这个迭代器将令牌存储在std::vector中。std::vector有一个范围构造函数,它以两个迭代器为一个参数,并将第一个迭代机和第二个迭代程序之间的数据复制到std::vector

声明

std::vector token(std::sregex_token_iterator(line.begin(), line.end(), separator, -1), {});

定义类型为std::vector<std::string>的变量"token",拆分std::string并将token放入std::vector中。对于您的情况,我们将使用std::transform将您的字符串更改为整数。

非常简单。

下一步。我们想从文件中读取。该文件也包含某种相同的数据。相同的数据是行。

对于上面的内容,我们可以对类似的数据进行迭代。如果是文件输入或其他什么。为此,C++具有std::istream_iterator。这是一个模板,作为模板参数,它获取应该读取的数据类型,作为构造函数参数,它获得对输入流的引用。不管输入流是std::cin,还是std::ifstream或std::istringstream。所有类型的流的行为都是相同的。

由于我们没有SO文件,我使用(在下面的示例中)std::istringstream来存储输入的csv文件。当然,您可以通过定义std::ifstream-csvFile(filename)来打开文件。没问题。

现在,我们可以读取完整的csv文件,并将其拆分为令牌,只需定义一个新变量并再次使用范围构造函数即可获得所有数据。

Matrix matrix1( std::istream_iterator<ColumnProxy>(testCsv), {} );

这个非常简单的一行代码将读取完整的csv文件并完成所有预期的工作。

请注意:我使用的是C++17,可以在没有模板参数的情况下定义std::vector。编译器可以从给定的函数参数中推导出参数。这个特性被称为CTAD("类模板参数推导")。

此外,您可以看到我没有明确使用"end()"-迭代器。

这个迭代器将由空的、带大括号的、具有正确类型的初始值设定项列表构建,因为由于std::vector构造函数需要,它将被推断为与第一个参数的类型相同。

希望我能回答你的基本问题。请参阅下面的完整C++示例:

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>
std::istringstream testCsv{ R"(1 2
3 4
5 6 7
8 9 10
)" };

// Define Alias for easier Reading
//using Columns = std::vector<std::string>;
using Columns = std::vector<int>;
using Matrix = std::vector<Columns>;
// The delimiter
const std::regex re(" ");
// Proxy for the input Iterator
struct ColumnProxy {
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {
// Read a line
cp.columns.clear();
if (std::string line; std::getline(is, line)) {
if (!line.empty()) {
// Split values and copy into resulting vector
std::transform(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
std::sregex_token_iterator(),
std::back_inserter(cp.columns),
[](const std::string & s) {return std::stoi(s); });
}
else {
// Notify the caller. End of matrix
is.setstate(std::ios::eofbit | std::ios::failbit);
}
}
return is;
}
// Type cast operator overload.  Cast the type 'Columns' to std::vector<std::string>
operator Columns() const { return columns; }
protected:
// Temporary to hold the read vector
Columns columns{};
};

int main()
{
// Define variable matrix with its range constructor. Read complete CSV in this statement, So, one liner
Matrix matrix1( std::istream_iterator<ColumnProxy>(testCsv), {} );
// Reset failbit and eofbit
testCsv.clear();
// Read 2nd matrix
Matrix matrix2(std::istream_iterator<ColumnProxy>(testCsv), {});
return 0;
}

再次:真遗憾没有人会读这篇。