当前位置:   article > 正文

用C++语言实现Python的input()函数_c++ input

c++ input

刚开始学编程的时候我使用了Python,用惯了Python的众多内建函数(所以它才会被称作为“具备电池的语言 battery-included language”),转型到C++的时候难免会有些不适应。今天就让我们实现一下那些“被抹灭掉的函数”,顺便帮一下像我一样刚刚踏入C++的世界的时候的初学者们吧。

为什么这是第一篇文章,实现的是input函数而不是最常用的print函数,或者是英语字母顺序开头的abs函数呢?这其实是有个人原因的:我初次踏进C++的土地时,第一次折磨我的函数便是input——我哪儿都找不到相应的实现。我觉得应该也有人跟我一样。

今天的目标

  • 能够使用C++实现Python内建的input()函数的所有功能。
    • 包括prompt提示符;
    • 回传值等等。

温故而知新

首先我们得知道Python的input函数带给了使用者什么的功能(说白了就是它是用来干什么的)。参考Python的官方文档,他写着:

如果存在prompt实参,则将其写入标准输出,末尾不带换行符。接下来,该函数从输入中读取一行,将其转换为字符串(除了末尾的换行符)并返回。当读取到EOF时,则触发EOFError。例如:

>>> s = input('--> ')
--> Monty Python's Flying Circus
>>> s
"Monty Python's Flying Circus"
  • 1
  • 2
  • 3
  • 4

如果加载了readline模块,input()将使用它来提供复杂的行编辑和历史记录功能。
引发一个审计事件builtins.input附带参数prompt
在成功读取输入之后引发一个审计事件builtins.input/result附带结果。

Python的官方文档可能用了机器翻译,有些小伙伴们可能看不懂这种由英语硬生生翻译成中文的语文。让我们解刨一下原文,再把它用大白话说出来。首先让我们看看文档的原文

If the prompt argument is present, it is written to standard output without a trailing newline. The function then reads a line from input, converts it to a string (stripping a trailing newline), and returns that. When EOF is read, EOFError is raised. Example:

>>> s = input('--> ')
--> Monty Python's Flying Circus
>>> s
"Monty Python's Flying Circus"
  • 1
  • 2
  • 3
  • 4

If the readline module was loaded, then input() will use it to provide elaborate line editing and history features.
Raises an auditing event builtins.input with argument prompt before reading input
Raises an auditing event builtins.input/result with the result after successfully reading input.

这什么意思呢?根据我们自己的翻译,其内容大意(大致上)是:

  • 如果prompt值非零,且内容为可打印(printable)字串(string)(也就是prompt可以用在print()函数里头),这个函数首先会打印prompt出来;
  • 然后Python会让你输入行字串(记住,就一行),当你按下回车(Enter键/return键)输入就会立刻终止。
  • 最后这个函数会以字符串的形式返回所有你刚才所输入的字符,不包括结尾的回车字符/换行字符'\n'/'\r'

剩下的可以管也可以不管。但是通常现在的终端机模拟APP(terminal-simulating application)已经帮你管好了。所以我们就一切从简。

最后让我们看看他的官方文档字串(docstring)的参数列表:

input(prompt='', /)

懂Python的朋友都应该知道,prompt参数后面的那个斜杠(slash)是为了美观而加上去的,直译器会把这个部分忽略掉。

着手开始实现

好了那么我们先准备一下我们的C++编译器,好让我们在编程完毕后顺利编译。不管你们用的是自由软体基金会的GCC/G++编译器也好,微软的Visual C++ (MSVC) 编译器也罢,只要支持c++11以及能够能产生可运行文件(executable)便行。我们这次实现对于编译器的要求并不是太高。

引入头文档

在C/C++的世界里,头文件决定一切。当你什么头文件(header file)都不引入(include)的时候,你顶多只能声明(declare)变量(variable)、函数(function)、类(class)等,做做四则运算(arithmetic operation)而不能输出结果。甚至Python、Ruby等语言是内建(builtin)的函数跟类,在C++中你也必须要#include <某个头文件.h>

我们这次要引入的头文档也不算太多,如果是C++的话就只需要两个:

#include <iostream>
#include <string>
  • 1
  • 2

iostream,表面意思为“输入/输出流”(input/output stream),“流”是指从输入/输出设备中读写到的字符。这个头文档对一个名为basic_iostream的模板(template)定义,实现了格式化的输入/输出。

string,顾名思义,这个头文件定义了C++中字串符的操作,跟Python的内建str类有异曲同工之妙(不同点是使用C++的std::string类需要导入,也就是像Python的import指令差不多;Python允许程序自由使用单引号''/双引号""作为字串的定义工具,C++只允许双引号""因为单引号是用来定义字元char类的,这些我们以后有机会细聊)。

撰写程式代码

那么,我们现在终于可以开始变成了。

定义一个input()函数

Python的input()是在接收完用户的输入之后,以字符串的形式返回的嘛(如上所述)。C++中的字符串类是std::string,于是返回值也一定是std::string

C++定义一个函数的方法是:[函数回传值] [函数名称,不能与其他变量、类、枚举、模板等有冲突] ( [参数类型,如有] [参数名称,如有。不能和其他参数名字一样,但是可以与变量、类等名称发生冲突,不过个人不建议这么做] ) { ... }

所以,代码如下:

std::string input(std::string prompt = "")
{
}
  • 1
  • 2
  • 3

由于Python input(prompt)的默认(default)参数(parameter)是空字串"",所以我们的C++程序也要效仿Python。

输出 prompt

不管程序有没有在呼叫(call)这个input()函数时有没有指明prompt参数,我们还是得打印prompt的。Python的内建打印方法print,可以用C++的std::cout代替(不过不完全能,以后我们逮住机会再实现一下print函数,还是挺有学习价值的),代码如下:

std::string input(std::string prompt = "")
{
    std::cout << prompt;
}
  • 1
  • 2
  • 3
  • 4

C++的std::cout默认输出后不带换行符,这对我们来说是个福音——因为不用再进行调整了。

到这里,程序应该能够把prompt打印至终端(或者其他输出设备)上。然后就没它什么事了。

留意这个时候千万不能使用input函数的回传值。由于我们没有明确地(explicitly)指明这个函数该回传什么。正常来说,回传值为void函数(也就是Python里的-> None函数)在呼叫完毕后会return;,也就是不回传任何值。直接访问这个函数的回传值会导致错误。但是由于我们主动把它设成一个回传值为std::string的函数,在编译时如果把它储存到一个变量还尚且没问题。但不管程序是直接还是间接地访问(access)这个变量(例如想知道其内容)或者这个函数的回传值,都会激发一个名为记忆体区段错误(segmentation fault)的运行时错误(runtime error)。程序戛然而止,不管现在正在运行什么重要的线程(thread)。显然我们跟用户都不想这种事情发生,也难怪编译器在编译时会蹦出来一条警告:Non-void function should return a value(“非无返回值的函数应该要返回一个值”)。

那,我们该怎么办呢?别急,我们现在正要去实现其功能。

输入功能实现

呼叫input()函数通常只有一个目的:输入字符。首先让我们看一看全部代码:

std::string input(std::string prompt = "")
{
    // Prints the `prompt` out in the console.
    std::cout << prompt;

    // Defines a variable, storing the user input.
    std::string source = "";
    // Retrieve the user input, by using the `std::getline` function and
    //     the `std::cin` method, with the new input value stored in the
    //     `source` variable.
    std::getline(std::cin, source);

    // And finally, return the `source` variable.
    return source;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 使用字符串类型的source变量来储存用户的输入字符。
  • 使用std::getline来获取用户的输入字符。
  • 最后,返回source

小伙伴们可能会问:网上的教程都写着std::cin >> 什么什么的,为什么我们不用。这其实涉及到某些原因:getline把所有用户的输入全都变成字符串,刚刚好与我们今天的目标一样,也与Python的设计理念相似。反观std::cin,只要提供变量名称,你输入整数、浮点数都可以;这个并不利于我们的程序:隐式(implicit)类型转换令变量不易回传——它可以什么都是,并不单单局限于std::string类。

实现 main函数

不想某些脚本语言(scripting languages),在一个标准的C/C++程式里,你不能直接在全局命名空间域(global namespace)里头撰写程式码,否则会招惹错误。你一定得写个int main()函数的实现(一说是void main()也行,但是标准C/C++建议使用int作为回传值并且在函数的最后回传0),相当于Python的if __name__ == "__main__"了。在我们这里,我们效仿上面Python官方档案的标准例子:

int main()
{
    // Calls the input function.
    std::string s = input("--> ");
    // Prints the return value, with a trailing newline.
    std::cout << s << std::endl;
    // Returns 0 to the operating system.
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

记得在std::cout的后面加上std::endl,因为不想print函数,C++的std::cout不会帮程序自动加一个分行符号\n。你要自己加上去才行,否则会很难看。还有也要记得回传0给作业系统(或者那个线程)。

成果

现在让我们来看看成果:

完整的代码

代码如下图,如下:
完整的代码

运行效果

接下来,请把这些代码储存,然后使用编译器来编译(我使用的时g++编译器)。留意g++在微软的Windows操作系统中,默认它不在环境变量当中。如果是如此,你得输入这个编译器的绝对路径(absolute path),例如C:\<一大堆>\g++.exe什么的。或者你可以使用控制台把它加入至环境变量当中。具体办法可以网上搜搜看,这里就不作赘述了。
苹果macOS的用户也请留意,使用g++之前你一定要先装Xcode并且下载Xcode命令列界面工具(Xcode Commandline Tools),才能正常使用g++编译器。

废话不说,这是我们在Windows上运行的小成果:
C++编译成果可以与Python的作对比,发现其实差的不算太多。
Python的标准
其实也是一样的嘛。

后记

为什么不用using namespace std;

稍微懂C++的朋友一定见过这句指令:

using namespace std;
  • 1

这句指令的意思是说,我们不用std命名空间了,我们把它里边所定义的所有变量跟函数都挪过去全局命名空间里。这样做乍看之下好像没有那么累赘了,确实是没那么累赘写代码也省事,但是它的道理跟Python的from 模组名称 import *是一样的:若不管理好引入的头文档资料,新来的定义会把之前的给覆盖掉。这样做非常麻烦,原本省事的目的没达到还惹了一连串的duplicate reference或者invalid name错误。最直白的例子是如果你用<windows.h>头文档,当中有minmax两个函数,是用来进行仅适用于Windows平台的大小比较作业。当你又手痒引入了<cmath.h>头文档,然后用using namespace std;作死,估计要么就是cmath.h里的minmax不能用;要么就是windows.h里的不能用,要么就是报错。还好这次的小项目十分小,声明一下也无妨,但我想表达的是这真不是编程上的一个良好习惯。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/548222
推荐阅读
相关标签
  

闽ICP备14008679号