我刚开始学编程时,写出来的第一个能跑的程序是“Hello World”,但真正让我觉得“我会写代码了”的,反而是后来在屏幕上输出了一行莫名其妙的“90!”。这个练习看起来简单到幼稚,但它几乎把新手期的所有基础问题都暴露了一遍:文件怎么保存、命令怎么执行、字符串为什么要加引号、中文感叹号为什么会在终端里变成乱码……
这篇是这个系列的第一篇,我不会只给你一个“照着敲一遍就能跑”的答案,而是想带着你把“输出90!到屏幕上”这件事彻底拆开。你可能会发现,一行看似简单的输出,背后其实藏着一整条链路:程序、标准输出、终端、编码、换行、缓冲。把这些链路搞清楚,后面学变量、输入、循环、函数都会轻松很多。适合零基础刚入门的读者,也适合已经写了几百行代码、但对“输出”背后的原理仍然模模糊糊的人。
1. 把“输出90!”拆开:先理解“屏幕”和“输出”到底是什么
1.1 你写的print/printf,不是直接往显示器上写字
很多人第一次写print("90!")的时候,会默认屏幕上出现这行字是“理所当然”的。其实不是。程序并没有直接控制你的显示器,它做的是另一件事:往一个叫“标准输出”的地方写了一段数据,也就是一串字节。终端或控制台程序读到了这串字节,再把它翻译成字符,显示在屏幕上。
这个过程可以类比成寄信。你在代码里写print("90!"),相当于把一封写着“90!”的信交给了邮局。你不需要知道信是怎么被送到收件人手里的,但邮局、交通工具、收件人的解读方式,都会影响对方最终看到的内容。这里的“邮局”是操作系统和终端,“收件人”就是屏幕前的你。
理解这一点很重要,因为后面你会遇到很多奇怪现象,比如程序明明执行了,屏幕上却什么都没显示;或者输出没有出现在终端,而是跑到了某个文件里;又或者屏幕上出现了一串乱码。这些问题的根源,几乎都在“输出链路”上,而不是你写的代码本身。
1.2 为什么“输出90!”比Hello World更适合当入门练习
传统的第一个程序是“Hello World”,我有段时间也这么教人。后来发现,纯英文的Hello World有一个“缺陷”:它太顺利了。你保存文件、运行、看到结果,好像什么都没发生,于是很容易跳过细节。
但“输出90!”不一样。它里面有一个数字“90”,有一个中文全角感叹号“!”。数字和字符混在一起,引出一个基础但关键的问题:90是数字,90!却是一个字符串。全角感叹号又引出一连串关于编码、输入法、中文标点的问题。这些问题对于新手来说很现实,因为每个人迟早都会遇到。
所以这个练习的完整目标可以拆成三层:
- 第一层:让程序跑起来,在屏幕上看到“90!”。
- 第二层:理解你正在输出的是一个字符串,而不是一个数字或运算式。
- 第三层:搞清楚中文全角字符在计算机里是怎么存储的,为什么有时候会乱码。
这三层正好对应了入门阶段最需要建立的三个认知:运行环境、语法基础、字符编码。一个简单的输出练习能装下这么多东西,是我选它做系列第一篇的真正原因。
1.3 标准输出不是“屏幕专属”,它还能变成文件
再说一个经常让新手懵掉的概念:标准输出(通常叫 stdout)。你以为print是“输出到屏幕”,但从系统的角度看,它只是把数据写到了一个“默认输出通道”上。这个通道默认连接着终端,所以你会看到屏幕上有字。但它也可以被重定向,连接到一个文件、一个管道,甚至是另一台设备。
在命令行里,这个操作非常直观:
python output.py > result.txt运行完之后,屏幕上什么都没有,但result.txt文件里出现了“90!”。同样一行代码,换个运行方式,输出就从屏幕变成了文件。理解了这点,你以后看日志、写脚本、做自动化,就会明白“输出”并不是一个只能在屏幕上出现的魔法,而是一个可以被自由引导的数据流。
2. 动手前的准备:装环境、选编辑器、定编码
2.1 最省心的起步组合是什么
在正式开始“输出90!”之前,先把工具准备好。我不建议新手一开始就装一个大而全的图形化开发工具,因为你会在界面配置上花掉大量时间,反而忽略了代码本身。最省心的组合是:一个顺手的文本编辑器 + 一个命令行终端 + 对应语言的运行环境。
文本编辑器怎么选?找一个轻量、免费、默认支持 UTF-8、能看到行号和缩进的就行。很多系统自带编辑器也能用,但体验不是很好。这个阶段千万不要在编辑器上纠结太久,能写代码、能保存文本,就够用了。
命令行终端是另一个必备工具。Windows 上常见的是命令提示符或 PowerShell,macOS 和 Linux 上通常叫终端。你要学会在终端里执行命令,因为大多数编程语言都需要在终端里运行或编译代码。打开终端、进入目录、运行文件,这组动作是新手的第一道坎。
2.2 把三件套统一到UTF-8,能避开一半的乱码问题
初学者最容易忽略的一件事是“编码一致性”。很多乱码问题,并不是代码写错了,而是文件保存格式、终端的解码规则、代码里的字符串内容这三者之间没有对齐。
这里我提前给你一个铁律:代码文件保存成 UTF-8;终端解码也尽量用 UTF-8;如果是网页,就在 HTML 头部声明<meta charset="utf-8">。把这三处统一,中文和中文标点的乱码问题可以消灭掉一大半。
为什么要强调这个?因为“90!”里的感叹号不是 ASCII 字符。ASCII 只规定了英文字母、数字、英文标点的编码,中文全角感叹号并不在其中。为了让中文标点能正常显示,文件在保存时必须把字符转换成 UTF-8 字节,终端在显示时也必须按 UTF-8 来解读。两边各说各话,屏幕上就只会出现一片奇形怪状的字符。
2.3 两个命令验证环境是否可用
在写第一个程序之前,先在终端里验证一下运行环境。比如你打算用 Python,就输入:
python --version如果能看到类似Python 3.x.x的版本信息,说明安装没问题。Windows 上如果输python没反应,可以试试py --version。C 语言编译器可以检查gcc --version,JavaScript 环境可以检查node --version。
这一步看起来多余,但它能帮你区分“环境没装好”和“代码写错了”这两种完全不同的情况。如果命令本身都找不到,问题一定出在环境,而不是代码。先确认环境,再写代码,可以少很多无意义的自我怀疑。
2.4 目录和文件名,建议一开始就规范
我见过很多新手把文件命名为“输出.py”或者“我的程序(最终版).py”。这其实不是不能用,但在命令行里输入包含中文、括号、空格的路径,很容易出错。命令行里处理空格需要转义或加引号,处理中文又要考虑系统编码,完全是给自己增加难度。
我建议你新建一个纯英文的目录,比如learn,文件也取英文名:output.py、output.c、output.js,或者干脆叫test.py。等掌握了命令行基础之后,你再随便怎么命名都行,但入门阶段,越简单越好。
3. 核心实现:四种方式把“90!”送到屏幕
3.1 Python:print("90!") 是最直观的写法
先看最简版本。新建一个output.py文件,输入下面这一行代码:
print("90!")然后在终端执行:
python output.py如果没有意外,屏幕上会出现“90!”。这一行代码看似简单,里面其实有三个关键点:
print是一个内置函数,负责把内容输出到标准输出。- 括号表示“调用函数”,让
print立刻执行。 "90!"是一个字符串字面量,双引号把里面的内容标记成“文本”。
如果你把代码改成print(90!),就会报语法错误。原因是90!不是一个合法的表达式:90可以是数字,但中文感叹号不是数字,也不是运算符,编译器根本不知道你写的是什么。只有把它放在引号里,变成一个字符串,才能被正常输出。
顺便说一句,Python 里单引号和双引号都能表示字符串,所以print('90!')也同样正确。你只需要保持前后一致,不要左边用双引号、右边用单引号就行。
3.2 C语言:printf("90!\n") 的隐藏细节
C 语言稍微麻烦一点,但它能让你看到更多底层的东西。新建output.c,输入:
#include <stdio.h> int main(void) { printf("90!\n"); return 0; }然后在终端里编译运行:
gcc output.c -o output ./outputWindows 上编译后会生成output.exe,运行output或output.exe即可。
这段代码里,#include <stdio.h>是把标准输入输出头文件包含进来,否则编译器不认识printf。main是程序的入口,程序从这里开始执行。printf负责格式化输出,"90!\n"表示要输出“90!”以及一个换行。return 0;则告诉操作系统“程序正常结束”。
注意,C 语言里printf不会自动换行。如果你省略\n,程序执行后终端可能会把下一行命令紧跟在结果后面,看起来很不舒服。另外,printf的第一个参数是可以带格式占位符的,比如printf("%s\n", "90!"),但初学阶段直接用字符串常量就够了。
C 语言在中文编码上比 Python 更敏感。源代码文件保存成什么编码,编译出来的程序里就存什么编码的字节。Windows 老版本控制台默认用的是 GBK 编码,如果你保存成 UTF-8,就可能出现乱码。后面我会专门讲怎么处理。
3.3 JavaScript:console.log 不是网页,控制台才是“屏幕”
如果你走的是前端方向,输出“90!”这件事会更微妙。先用 Node.js 运行环境新建一个output.js:
console.log("90!");在终端执行node output.js,控制台会打印“90!”。这和 Python 的print非常像,通常也叫“打印到控制台”。
但如果是在浏览器里,情况会不同。你把这样一段 HTML 代码保存成index.html,用浏览器打开:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>输出90!</title> </head> <body> <script> console.log("90!"); </script> </body> </html>你会看到页面上什么都没有,只有在按下 F12 打开开发者工具、切到控制台标签页之后,才发现“90!”出现在日志里。
这是因为console.log输出的是“控制台日志”,它不是页面的一部分。如果你想让“90!”真的显示在网页上,需要用 DOM 操作,把文本塞进页面元素里:
<p id="output"></p> <script> document.getElementById("output").textContent = "90!"; </script>这个例子特别适合帮初学者理解“输出到屏幕”并不只有一种方式。在终端程序里,标准输出连接的是控制台;在网页里,页面显示和开发者控制台是两套完全不同的体系。如果你以后想学前端,一定要分清这两者。
3.4 Bash:echo "90!" 和 printf "90!\n" 的最短路径
如果连文件都不想新建,直接在终端里输入命令也可以:
echo "90!"或者:
printf "90!\n"echo是最简单的输出命令,它会输出参数内容并自动换行。printf则更接近 C 语言里的printf,格式控制更强,换行需要显式写\n。
这里有一个很小的坑:在交互式 Bash 终端里,半角感叹号!有特殊含义,可能触发历史命令展开。如果你拿的是英文字符串"90!",最好用单引号包起来:
echo '90!'而题目要求的是中文全角感叹号“!”,不是半角!,所以一般不会触发历史展开。但养成给参数加引号的习惯总没坏处,尤其当字符串里有空格或特殊字符时,引号能避免 shell 把它们拆成多个参数。
Shell 命令也可以写进脚本文件,比如output.sh,然后通过bash output.sh执行。这样它就从“一条命令”变成了“一个程序”。
3.5 四种语言的对照小结
到这里,你应该已经知道,所有语言做的事其实是同一件事:把字符串“90!”写入标准输出,让终端帮忙显示。只不过每种语言叫法不同、底层细节不同。
| 语言 | 核心代码 | 运行方式 | 是否自动换行 | 中文标点注意点 |
|---|---|---|---|---|
| Python | print("90!") | python output.py | 默认换行 | 源文件保存为 UTF-8 |
| C | printf("90!\n") | gcc output.c -o output && ./output | 不自动换行,需\n | 编译器与终端编码需一致 |
| JavaScript | console.log("90!") | node output.js | 默认换行 | 浏览器控制台与页面显示要分清 |
| Bash | echo "90!" | 终端直接执行 | echo 自动换行 | 注意引号与历史展开 |
看到这张表,你应该能明白:不要死记“某一种语言怎么输出”,要抓住“输出到标准输出”这个共同的本质。换一门语言,只是换了个说法和格式而已。
4. 深入细节:为什么中文感叹号会变成乱码?从字符编码说起
4.1 字符不是字节,但最终要变成字节
“90!”这三个字符,在你眼里是三个字。但在计算机内部,一切都是字节。你需要某种规则,把字符映射成字节,这个规则就叫“字符编码”。
最简单的 ASCII 编码规定了英文字母、数字、英文标点怎么存。比如字符9对应十六进制字节0x39,字符0对应0x30。这两点基本是全世界通用的。
但中文全角感叹号“!”不在 ASCII 范围内,它来自于 Unicode 编码表,码点是 U+FF01。在 UTF-8 编码规则下,这个字符会被编码成三个字节:0xEF 0xBC 0x81。而在旧的 GBK/GB2312 编码规则下,它会被编码成两个字节:0xA3 0xA1。
你可以把字符理解成“人”,把编码理解成“车牌号”。同样的一个人,在不同的城市可能需要不同的车牌号。如果某个系统用一套规则给这个人办了牌,另一个系统却按另一套规则去查询,自然就查不到正确的人,于是显示出来一团乱码。
4.2 用一行Python证明“90!”到底占多少字节
理论听起来容易忘,不如亲手验证一次。在 Python 里输入这几行:
s = "90!" print(len(s)) # 3 print(len(s.encode("utf-8"))) # 5 print(s.encode("utf-8").hex()) # 3930efbc81你会看到:
len(s)是 3,因为 Python 按“字符数”计算,字符串里有9、0、!三个字符。len(s.encode("utf-8"))是 5,因为两个数字各占 1 字节,全角感叹号占 3 字节,加起来 5 字节。hex()输出3930efbc81,这正好是0x39、0x30、0xEF、0xBC、0x81的十六进制拼接。
C 语言里还藏着一个更反直觉的事实。字符串最后会有一个不可见的\0结束符,所以“90!”在内存里实际占用 6 个字节。用strlen和sizeof可以看出来:
#include <stdio.h> #include <string.h> int main(void) { printf("%zu\n", strlen("90!")); // 5 printf("%zu\n", sizeof("90!")); // 6 return 0; }strlen数的是有效字符的字节数,是 5;sizeof数的是数组总大小,把结尾的\0也算进去,是 6。很多学过 C 的人都容易在这上面愣一下。能感受到这一点,你就已经比只会写print的人多理解了一层。
4.3 乱码“90锛”“90ï¼”到底是谁的锅
乱码的成因,本质就是“编码和解码不一致”。具体到“90!”,最常见的两种情况是:
第一种,文件保存成了 UTF-8,但终端按 GBK 或者别的编码去解码。UTF-8 编码“!”的0xEF 0xBC 0x81在 GBK 环境中会被解读成别的字符,你看到的就是类似“90锛”或者“90ï¼”的诡异结果。
第二种,文件保存成了 GBK,但终端或程序按 UTF-8 去读。同样道理,字节对不上,屏幕上就会出现�或者解码失败的错误提示。
排查方法也很简单,没必要靠猜。看三个地方:编辑器右下角或设置里的文件编码;终端当前的编码;如果是网页,看 HTML 开头有没有<meta charset="utf-8">。把这三处都调成 UTF-8,再运行一次,绝大多数乱码问题都会消失。
Windows 终端里有个常用命令是chcp 65001,其中 65001 就是 UTF-8 代码页。执行之后再运行你的 Python 或 C 程序,乱码通常会迎刃而解。Linux 和 macOS 的终端基本默认 UTF-8,所以很少需要手动切。
4.4 全角感叹号与半角感叹号,别混用
这里还想提醒一个细节:全角“!”和半角“!”长得不一样,占的显示宽度也不一样,在计算机里完全是两套字符。题目里要求的是中文全角“!”,所以字符串里必须输入全角符号。如果你敲代码的时候输入法没切好,一不小心输成了半角"90!",程序能跑,但输出内容和题目就不一致了。
怎么确认自己敲的是哪个?最简单的方法是用十六进制查看工具。在 Unix 类系统里,可以这样:
printf '90!' | xxd如果输出里的字节是3930 efbc81,说明全角感叹号已经正确输入。如果只有3930 21,那说明你用的是半角感叹号!。这个技巧平时用不太上,但当你怀疑“代码看着对,输出却不对”的时候,它能带你快速看清楚字节真相。
5. 新手最容易踩的坑:报错与排查手册
5.1 引号和括号变成全角,导致语法错误
新手写代码时最常见的报错,不是逻辑错,而是输入的标点不对。你以为自己写的是print("90!"),实际编辑器里可能是这样的:
print("90!")注意看,前面的左括号是中文全角括号(。Python 解释器并不认识这个字符,所以会直接报语法错误,甚至提示“invalid character '(' (U+FF08)”。
另一种更隐蔽的情况是引号不匹配。比如字符串左边用了半角双引号",右边却用了全角右引号”,或者反之。程序同样会一脸迷茫,因为它根本找不到一个正确的字符串结束位置。
解决这个问题的唯一根治办法是:写代码时把输入法切到英文模式,所有括号、引号、分号、逗号都用英文半角符号。刚开始可能会频繁踩坑,踩过几次之后,你会练出一种条件反射:看到报错,第一反应就是检查自己有没有把全角标点混进代码里。
5.2 输出乱码,可能是编码不一致
乱码的报错并不一定写在终端里,它通常直接以“结果不对”的形式出现。比如你运行后看到“90锛”,而不是“90!”。这时候不要急着改代码,先按顺序排查:
- 检查文件编码:用编辑器把文件重新保存为 UTF-8。
- 检查终端编码:Windows 下试试
chcp 65001。 - 如果用的是 C 语言,还要确认编译器有没有按 UTF-8 读取源码。
一个很常见的场景是:你在 Windows 上用老式记事本编辑源码,记事本可能默认保存成 ANSI/GBK,Python 3 默认用 UTF-8 读取源码,于是程序还没开始运行就报编码错误。解决办法就是把文件另存为 UTF-8 格式。很多文本编辑器在“另存为”时都有编码选择,选 UTF-8 即可。
5.3 黑窗口一闪而过,程序到底跑没跑
Windows 上双击一个.py文件或.exe文件,有时会看到黑色窗口一闪而过,根本来不及看清输出。这通常不是代码有问题,而是程序运行结束后,窗口自动关闭了。
解决办法不是去代码末尾加input()或system("pause"),而是先打开终端,手动进入文件所在目录,再运行程序。比如:
cd learn python output.py这样终端窗口会一直留在那里,你就能看到输出结果。我特别不建议新手在代码里硬塞system("pause")这种写法,它会掩盖程序真实的状态:如果后面你的程序崩溃了,暂停函数可能让你误以为它还在正常运行。学会在终端里运行程序,才是正道。
5.4 浏览器里看不到想要的输出
如果你用的是 JavaScript,并且打开了 HTML 文件,发现页面上什么都没有,先别急着怀疑代码。
最常见的原因是console.log把内容输出到了开发者工具的控制台,而不是网页本身。你需要按下 F12,找到“控制台”或“Console”面板,才能看到“90!”。如果想让用户直接在页面上看到,就得修改页面 DOM,比如把文本赋给某个元素的textContent。
<p id="output"></p> <script> document.getElementById("output").textContent = "90!"; </script>这个例子提醒你:在编程的世界里,“屏幕”这个词有很多种解释。终端是一种屏幕,网页是一种屏幕,日志面板也是一种屏幕。先搞清楚你的程序输出给了哪块屏幕,再决定用什么 API。
5.5 常见问题速查表
| 现象 | 最可能原因 | 处理方法 |
|---|---|---|
Python 报SyntaxError | 全角括号、全角引号混入代码 | 切英文输入法,重写所有标点 |
| 输出是“90锛”或“90ï¼” | 文件编码与终端解码不一致 | 统一为 UTF-8,Windows 下先chcp 65001 |
| 运行 Python 提示“No such file” | 终端当前目录不在文件所在位置 | 用cd进入文件目录,或用ls/dir确认文件 |
| 窗口一闪而过 | 程序执行完,终端窗口自动关闭 | 先打开终端,再手动运行程序 |
| 网页显示空白 | console.log不会写入页面 | 改用 DOM 操作,或打开开发者工具查看日志 |
| C 程序输出乱码 | 源码保存编码与控制台代码页不一致 | 保存为 UTF-8,并把终端代码页切换到 65001 |
这张表可以先存着。以后遇到类似问题,按表对照一遍,比自己瞎改省时间得多。
6. 一次完整的排查实录:从报错到屏幕上出现“90!”
6.1 现场还原:一个典型的新手流程
我假设一个场景:某同学刚开始学 Python,他在编辑器里写了代码,但运行时出现了报错。代码写的是:
print("90!")报错信息指向(这个字符。此时不需要去改逻辑,只需要把左括号换成英文半角(。改成:
print("90!")然后再运行。这次不报错了,但 Windows 终端里显示出来的是“90锛”或者乱七八糟的字符。按照前面的排查思路,他先执行chcp 65001把终端切到 UTF-8,再重新运行:
chcp 65001 python output.py这次屏幕上终于出现了“90!”。整个排查过程只做了两步:修正全角标点、统一编码。但很多新手会卡在这里很久,因为他们总以为是代码逻辑的问题,反复调整print的参数,却没想到真正的坑在代码之外的标点符号和编码设置里。
6.2 排查思路背后的方法论
从这个小案例里,可以提炼出一个通用排查套路:分层定位。先分清楚问题出在哪一层,再针对那一层找原因。
- 代码层:检查语法、引号、括号、字符串定义。
- 文件层:检查保存编码、文件名、文件位置。
- 终端层:检查当前目录、终端编码、运行命令。
- 环境层:检查解释器或编译器是否安装、版本是否正确。
大多数入门期的报错,根本轮不到“算法逻辑”出场。你只要按这个顺序查,很多问题一下子就明白了。还有一个特别值得养成的习惯:造最小复现。把你认为有问题的代码删到只剩三五行,再运行。如果问题还在,说明核心代码有问题;如果问题消失,那就是之前某些多余代码干扰了结果。
7. 输出之外:这个练习还能怎么延伸
7.1 从“输出固定内容”到“接收输入再输出”
“输出90!”只是第一步。下一步很自然的问题是:如果我不想让内容写死在代码里,而是让用户输入内容,程序再输出,该怎么办?
Python 里可以这样写:
name = input("请输入名字:") print(name + ",90!")C 语言里可以用scanf,JavaScript 里可以监听输入事件。你会发现,一旦你理解了输出,输入也没有那么神秘。输出是数据从程序流向外部,输入则是数据从外部流进程序。一进一出,程序就开始变得有生命了。
7.2 从输出一行到输出三行:循环是下一个主题
如果想把“90!”输出三遍,你会怎么做?新手可能会复制三行print("90!")。但在循环出现之后,你会更愿意写成:
for i in range(3): print("90!")从“写死”到“循环”是编程思维的一次飞跃。而这次飞跃的前提,是你已经能稳定地把“90!”输出来。所以说,不要小看这个练习,它是你搭建后面一切能力的第一块砖。
7.3 标准输出和标准错误,是两个不同的通道
再往深一点,你还可以接触“标准错误”这个概念。标准输出(stdout)用于正常结果,标准错误(stderr)用于报错信息。在终端里,你可能看不出区别,但重定向时区别非常明显:
python output.py 2> error.txt如果程序把报错信息写到了标准错误,那么它会进入error.txt,而正常的“90!”仍然会输出到屏幕。理解这个机制,以后排查线上问题、看日志的时候,会省很多力气。
7.4 我的个人经验:把“第一个功能”做扎实
写了这么多年代码,我的体会是,很多人学编程学得急,总想赶紧跳到“开发一个项目”,却连最简单的输出都没吃透。说实话,“输出90!”这件事,你花十分钟能跑通,但如果你愿意多花一小时去折腾乱码、编码、缓冲区这些问题,收获会大得多。
最后再分享一个小技巧吧。当你怀疑某个字符、某个文件编码有问题时,不要只靠眼睛看。用xxd或hexdump看一下文件的实际字节,往往一眼就能定位问题。你眼里看到的是“90!”,但在字节层面可能早就变成了完全不同的东西。学会和字节打交道,你会发现之前那些玄学一般的乱码,其实都有非常明确的解释。