gorsonpy

导航

寒假第三轮作业(1) 学习如何利用命令行规范输入和输出(含有各种文件操作)

这个作业属于哪个课程 <福州大学2022面向对象程序设计>
这个作业要求在哪里 <2022面向对象程序设计寒假作业3>
这个作业的目标 优化第二轮作业程序算法及规范输入输出
作业正文 如下
其他参考文献 <在Linux系统终端下编译并执行C/C++代码><C语言流重定向的两种方式><通过输入输出重定向和windows批处理文件比较程序输出和正确输出><C/C++生成的exe文件如何传参数到main中>、《CSAPP》第一章

前言

  第二轮作业的输出输出采用的C++STL的fstream,当时的输入输出采用的是硬编码方式不符合题目的需求,第三轮作业在着手优化算法之前先探究一下输入输出(挺长的踩了很多坑,想看结论直接拉结尾就行)。
ps:作业的要求是这样的:
在这里插入图片描述

发现成果

  1. 和第二轮的猜想一样,这道题目要求的输入输出应该采用C风格的重定向而不应使用C++的STL。
  2. C语言的重定向分为两种,一种是oier可能很熟悉的freopen和fclose:
    在这里插入图片描述
    参考网上寻找的函数原型签名,可以看出这种和fstream所能达到的效果类似,不是我们所需要的。
  3. 第二种就是与题目要求类似的命令行方式。这种方式在代码上与标准输入输出相同,只通过添加参数运行命令的手段来达到重定向.注意我说的是类似,还是有不同之处.

具体细节

假设有一个从Add.cpp,代码如下:

#include<iostream>
using namespace std;
int main()
{
  int a, b;
  while (cin >> a >> b)
  {
    cout << a + b << endl;
  }
  return 0;
}

编译

Windows下cmd命令(不指定路径就得是在cpp目录下)

g++ -o AddNum.exe Add.cpp

表示在该目录下把Add.cpp编译成AddNum.exe。
其中.exe后缀名可省略。

运行

假设我们有in.txt和out.txt分别作为文件输入和输出。
Windows下cmd命令行(不指定路径就得是在exe目录下)

.\AddNum.exe <in.txt >out.txt

'>' 和 '<' 为输入输出重定向的符号。
上面我说类似是因为作业要求的是"./",但是实际上是".\",联想第一次作业学习的内容,"./"实际是Linux下命令行的语法。个人认为这个地方可能是写错了,也可能是作业本身就隐含要求要在Linux虚拟环境进行。除此之外cpp编译出的exe是需要有重定向箭头符号的,作业里的示例也没有,暂时不得而知原因。

以上操作均在cmd下进行,powershell会报错误(5.x/7.x).

在这里插入图片描述

还有一个最怪的地方就是题目要求以main.c编译出main.exe??? 推荐用c++完成,结果要求用.c后缀编译,到底是要求用c还是c++,哈哈哈,不太懂,太牛了不知道该咋做。

多文件编译

上面的简单例子没有额外引其他的包,如果像第二轮作业那样比较复杂的程序要引入其他cpp和.h文件该怎么办呢? 网上找了一番没找到类似的,亲手实验一波看看。尝试把加法功能写在一个函数里,通过头文件声明,源文件定义的方式来做,三个文件如下:

AddUtil.h

#pragma once
#ifndef ADDUTIL_H
#include<iostream>


int DoAdd(int x, int y);
#endif // !ADD_UTIL_H


do_add.cpp

#include<iostream>
#include"AddUtil.h"
using std::cout;

int DoAdd(int x, int y)
{
  return x + y;
}

Add.cpp

#include<iostream>
#include"AddUtil.h"
using std::cout;

int DoAdd(int x, int y)
{
#include<iostream>
#include"AddUtil.h"
using std::cin;
using std::cout;
using std::endl;

int main()
{
  int a, b;
  while (cin >> a >> b)
  {
    cout << DoAdd(a, b) << endl;
  }
  return 0;
}
}

在IDE中运行没毛病,如果像之前那样直接编译,就会报错找不到函数定义:
在这里插入图片描述
参考了《CSAPP》编译过程的环节,先分别

gcc -c Add.cpp
gcc -c do_add.cpp

把cpp先别成-o后缀的二进制文件。(头文件只起声明作用,无需变成-o文件)。
接下来再做链接。实际上所有的C/C++编译应该都包括预处理、编译、汇编、链接这四个阶段,只是单文件无需断开过程,多文件就相当于要手动去做最后一个链接过程。gcc -c 命令就是只进行到汇编阶段停止。

然后链接生成可执行文件main.exe

g++ Add.o do_add.o -o main.exe

成功:

在这里插入图片描述

试一试文件操作:

.\main.exe < in.txt > out.txt

在这里插入图片描述
靠谱,成功了。

Linux虚拟机下操作

  上文提过Windows下的命令跟作业要求的还是有些形式的差距,移步Linux虚拟机下看看是否能达到相同的效果。
在这里插入图片描述

非常遗憾的是虽然斜杠的位置确实吻合了,但是仍然需要有箭头作为重定向标志。而且一个致命的弊端在于,这样输入输出都只能有一个文件,但是作业要求的是两个。

exe传参数

  可以说正当我一筹莫展之际,我抱着试一试的态度重新搜索了几次,居然在一篇十年之前的文章找到了我想要的答案。
这道题目正确的输入输出做法应该是使用exe传参的方式,exe可以传参数到带参数的main函数里,这个真的很新奇了,我确实从来都不知道可以这样,所以一直没往这方面想。 测试代码如下:

#include<iostream>
using namespace std;

int main(int argc, char** argv)
{
  for (int i = 0; i < argc; ++i)
  {
    cout << argv[i] << endl;
  }
  return 0;
}

编译为main.exe后运行测试

在这里插入图片描述
完美!这样就可以在启动exe命令的时候把文件名称传入代码里,然后在代码里用fstream/freopen。如果再移步Linux环境下改变斜杠方向,那么就完全契合了作业的要求。

最终结论

  1. Linux和Windows命令行在C/C++文件编译上大致相同,只有细微的语法差异。

  2. 多文件编译先编译成二进制文件,再手动链接。

  3. 转化为文件输入输出操作大致有四种方式:
    1. C++ 的标准库fstream.
    缺点:硬编码,且fstream里的ifstream存在空行读取不定的问题。
    优点:C++正统的标准库,根据现代C++规范应尽量选择标准库实现所需功能....
    2. C风格文件重定向freopen,fclose
    缺点:硬编码
    优点:写起来比较简单,了解的人也比较多,且无需额外引入命名空间。
    3. C风格命令行方式的重定向通过'<''>'符号实现。
    缺点:输入输出分别最多只能指定一个。
    优点:写起来比较简单,代码内部无需使用特定文件操作,保持和控制台输入一样的就可以。
    4. (推荐)exe执行语句附带参数传入main函数+fstream/freopen&fclose.
    缺点:需要掌握前置知识,知道main函数也可以传入参数。
    优点:灵活度非常高,高效解耦。

  4. 要完成作业的要求,最佳方式是第四种:
    (Linux环境开发)修改main函数的函数签名为

    int main(int argc, char** argv)
    

    在代码中用循环遍历argv数组获取文件名,然后使用ifstream/freopen做文件操作,这样就解开了代码的耦合。当然根据作业要求输出总是固定为"res.txt"文件,输出的部分硬编码就行。

    运行的时候在Linux命令行:

    ./main 规则集名称 数据集名称
    

结语

  IDE确实很方便,但是还是应该多学习这种底层的操作方式(尤其是C/C++这种偏底层的语言),减少对于IDE帮助组织代码的依赖....这次的探索感觉收获还是很大的,虽然一开始踩了坑,但是也不失为一次好的学习机会.下一篇会着手修改原来的代码以匹配要求的输入输出,然后考虑优化算法前先加速一下输入输出.

posted on 2022-02-04 16:22  gorsonpy  阅读(149)  评论(0编辑  收藏  举报