【问题标题】:How to create an array given a line of input with integers where the first integer is the length?如何在给定一行输入的整数中创建一个数组,其中第一个整数是长度?
【发布时间】:2019-05-31 08:54:20
【问题描述】:

我正在尝试创建一个函数,该函数返回一个数组的地址,该地址是通过从用户那里获取单行输入而创建的,其中第一个整数是长度。例如,如果用户的输入是“5 19 8 26 12 9”,则相应的数组将为 [19, 8, 26, 12, 9](长度为 5)。

我这样做的方法是在函数运行后将其作为参数 (argc,argv) 例如从用户那里获取输入。 “./a.out 5 19 8 26 12 9”。我想知道是否还有其他可能的解决方案可能使用 scanf 或 cin...

【问题讨论】:

  • 如果你想使用cin但避免需要一个哨兵,只需让用户在一行中输入所有数字,用std::getline()提取整行,然后用@解析出整数987654323@.
  • @CruzJean 这与从一开始就使用 cin 解析整数没有什么不同。
  • @0x499602D2 它避免了必须手动检查换行符以停止提取值。替代方法是在序列前加上长度或终止哨兵。
  • @CruzJean OP 并没有说有多行输入。如果您已经知道格式,则不需要哨兵。
  • @CruzJean 给我们整数个数,以便我们知道何时停止。

标签: c++ arrays


【解决方案1】:

这是如何指定具有动态长度的数据项序列的范围的一般问题。

有趣的是,实际上并没有很多编程模式可以解决这个问题(尽管有很多方法可以实现它们)。这个问题实际上归结为一个简单的问题:我如何知道何时停止接受价值观?

当您收到已存储的值(例如 argc/argv 或传入数组或其他容器)时,您已经拥有这些值并且您知道有多少。这是小事一桩。从文件或终端解析数据是事情开始变得有趣的地方。

这里列出了常用的“停止机制”(据我所知,所有其他机制都是前缀和/或后缀的特定应用)。

前缀序列

这是你描述的方法。在最一般的意义上,它涉及在输入前加上一些东西,告诉你有多少数据。这可能是人类级别的元素数量,也可能是机器级别的,例如从网络连接接收的字节数。

这是一个使用输入序列中的元素数量作为前缀的示例。

std::vector<int> vec;
int count, temp;

std::cin >> count;
for (int i = 0; i < count; ++i)
{
    std::cin >> temp;
    vec.push_back(temp);
}

请注意,使用此方法您可以立即知道序列中的元素总数,您可以使用它来简化代码并减少动态分配的数量:

std::vector<int> vec;
int count;

std::cin >> count;
vec.resize(count); // performs only one allocation
for (int i = 0; i < count; ++i) std::cin >> vec[i];

用户输入示例(控制台或文件):

4 3 5
4 88 -90
2

结果值(vec):

3, 5, 4, 88

后缀序列

与前缀序列类似,您也可以为该序列添加一些值来表示停止的位置。这通常被称为终止符(例如,C 风格的字符串以零字节终止,即'\0')。

这个例子展示了如何读取一个非负整数序列。我们使用第一个负值作为停止条件:

std::vector<int> vec;
int temp;

while (std::cin >> temp && temp >= 0) vec.push_back(temp);

用户输入示例(控制台或文件):

4 3 5
4 88 -90
2

结果值(vec):

4, 3, 5, 4, 88

到达 EOF

如果您从文件中读取,EOF(文件结尾)可以用作停止机制。它本质上是系统特定的前缀和/或后缀组合。例如,正在使用的特定文件系统结构通常会以字节为单位存储文件的长度。这本质上是前缀方法(尽管在这种情况下前缀值实际上并没有出现在数据序列中)。

这种方法很受欢迎,因为文件只包含一系列相同类型的元素是很常见的。它可以与cin 一起使用,但前提是cin 已经从文件中重定向(否则你永远不会 - 传统上 - 到达终点,因为它只会以交互方式等待更多输入作为控制台会话)。

std::vector<int> vec;
std::ifstream file("nums.txt");
int temp;

while (file >> temp) vec.push_back(temp);

事实上,这种用法非常普遍,以至于 STL 添加了std::istream_iterator,以便以安全的、类似于库的方式轻松编写。在这种情况下,它并不是真的更好,但取决于你在做什么迭代器可以更方便 - 例如您可以将它们输入std::accumulate() 以获得总和。下面是一个使用std::istream_iterator 做同样事情的例子:

std::vector<int> vec;
std::ifstream file("nums.txt");

std::copy(std::istream_iterator<int>(file), std::istream_iterator<int>(),
    std::back_inserter(vec));

示例用户输入(文件):

4 3 5
4 88 -90
2

结果值(vec):

4, 3, 5, 4, 88, -90, 2

行尾

使用行尾(即'\n')作为停止条件在技术上是一种后缀序列方法,但它足够不同且足够普遍,我认为应该直接解决它,特别是关于你正在尝试的内容完成。

您可以使用未格式化的输入函数行std::cin.get() 手动搜索您要保留或忽略的字符,但这很繁琐且容易出错。我将要演示的方法比手动方法慢,但如果性能不是问题,它们会非常优越(尤其是对于具有重载流提取运算符的更复杂的类型)。

我们将从cin(以换行符'\n' 终止)读取整行文本,然后解析出它的值。

std::vector<int> vec;
std::string line;
int temp;

std::getline(std::cin, line);
std::istringstream ss(line);
while (ss >> temp) vec.push_back(temp);

如果您想使用标准实用程序(或者如果您需要迭代器):

std::vector<int> vec;
std::string line;

std::getline(std::cin, line);
std::istringstream ss(line);
std::copy(std::istream_itereator<int>(ss), std::istream_iterator<int>(),
    std::back_inserter(vec));

用户输入示例(控制台或文件):

4 3 5
4 88 -90
2

结果值(vec):

4, 3, 5

【讨论】:

  • 非常彻底的答案和非常好的格式。在遇到无效输入等情况下,只有std::cin.clear()std::cin.ignore (std::numeric_limits&lt;std::streamsize&gt;::max(), '\n'); 可以触及的其他要点等等。但这不会影响您的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-24
相关资源
最近更新 更多