ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

打印九九口诀表:多语言实现与格式化输出实战解析

打印九九口诀表:多语言实现与格式化输出实战解析 看到“4-24打印九九口诀表”这个题目时我脑子里第一反应就是这八成是某本实训教材第4章的第24题。不是因为它独特而是因为“打印九九乘法表”实在是太经典的练手题目了凡是学过一点编程的人基本都写过。别看它简单它其实串起了三层循环、格式化输出、边界条件三个基本功很多新手在这里栽跟头也有很多老手在这里翻车。先解释一下这里的“打印”。题目里的打印是编程术语指把内容输出到屏幕不是让你按CtrlP调出打印机。如果你学的是控制台程序你看到的是文本表格如果你做的是网页那还能真的通过浏览器的打印功能变成纸质文档。这两种“打印”后面我都会讲到。这篇文章适合谁适合正在学循环结构、准备期末上机考试、或者想复习格式化输出的同学。你不需要懂高深算法会基本的变量和循环就够了。我会给出C、Java、Python、JavaScript四种语言的完整写法再讲几个常用的变体最后把大家实际跑代码时遇到的常见问题列出来包括“系统打印服务已关闭”这种听起来和代码无关但又会卡住你的坑。全程都是我一个一个踩过、排过的经验直接拿去用就行。1. 题目全貌与解题思路1.1 标准的九九口诀表长什么样先把目标说清楚。题目要求打印的九九口诀表通常长这样1*11 1*22 2*24 1*33 2*36 3*39 1*44 2*48 3*412 4*416 …… 1*99 2*918 3*927 …… 9*981仔细观察这张表你会发现一个规律第1行有1个等式第2行有2个等式第3行有3个等式……第9行就有9个等式。每一行里第一个数被乘数从1开始一直增加到和行号相等第二个数乘数就是行号。比如第5行式子就是“15、25、35、45、5*5”。这种只有左下半部分的表格专业说法叫“下三角”。有些教材的题目还会要求在每一行末尾都不留多余空格或者要求整个表格用空格严格对齐。这些问题都不难关键是你得先搞清楚输出结构否则后面怎么写都不对。1.2 双层循环的边界到底怎么定有了上面的规律代码结构就很清楚了需要两层循环。外层循环控制行也就是乘数从1到9内层循环控制列也就是被乘数从1到当前行号。如果用变量 i 表示行号乘数用变量 j 表示列号被乘数那么核心逻辑就是for i 1 to 9: for j 1 to i: 输出 j * i i*j 换行为什么内层循环的结束条件是j i而不是j 9这就是这道题的精髓。如果是j 9那么每一行都会输出9个式子得到的是一张完整的9x9方阵而不是下三角。更重要的是方阵里会出现大量重复比如“199”和“919”都会出现这就不符合九九口诀表的日常习惯。我们平时背口诀只背“一九得九”不会把“九一得九”单独背一遍。所以内层循环的边界是由外层循环的值决定的这一点是解出这道题的关键。很多同学在这里想不明白写成固定值9结果输出出来总觉得怪怪的。1.3 输出格式里的三个隐形考点第一内层循环结束后必须换行。在C和Java里printf、print默认不会换行Python 的print默认换行但如果你把内层循环也写成print每个式子都会独占一行。所以一定要在每一行循环结束的位置单独输出一个换行符或者让内层循环只输出数据、外层循环再负责换行。第二式子之间要有分隔符。最常见的是制表符\t或者空格。如果你直接连着写输出会变成“111122 2*24”根本没法看。这事看起来小但几乎所有第一次写这道题的同学都会踩一次。第三涉及字符串拼接时要注意类型转换。比如Java里写System.out.println(j * i j * i)这里j * i是整数会先计算结果再拼接成字符串没问题。但如果你写成了j * i i * j运算顺序正常也还行。真正容易错的是在别的地方想把整数当字符串直接用结果得到字符的ASCII码之类的诡异数据。记住一个原则先把算好的数值放到字符串表达式里让它自动转换别把两个整数直接拼不然结果可能就是数字相加后的值。2. 多语言实现同一套逻辑不同脾气2.1 C语言版printf的格式化就是标准答案先给一个能直接跑的C语言版本#include stdio.h int main(void) { for (int i 1; i 9; i) { for (int j 1; j i; j) { printf(%d*%d%d\t, j, i, i * j); } printf(\n); } return 0; }这里printf里的%d是整数占位符后面依次对应 j、i、i*j。为什么这么写因为C语言没有内置的字符串拼接一切输出都靠格式化函数。\t是制表符在控制台里通常能起到对齐作用但注意不同终端下制表符宽度不一样所以真正的“像素级对齐”需要靠宽度占位符后面第三节再细讲。这段代码的输出是1*11 1*22 2*24 1*33 2*36 3*39 ……C语言的坑在于如果你在printf里少写了一个%d或者参数数量和占位符数量对不上轻则输出乱码重则出现未定义行为。所以在循环体里写printf(%d*%d%d\t, j, i, i * j);时一定要数清楚三个占位符三个参数一个不多一个不少。还有一个细节内层循环里的变量建议用j表示列号i表示行号这样阅读代码的人能一眼看懂。别用a、b代码短的时候无所谓代码一长就乱了。2.2 Java版System.out.printf与println的配合Java的写法跟C非常接近但用的是System.out.printfpublic class MultiplicationTable { public static void main(String[] args) { for (int i 1; i 9; i) { for (int j 1; j i; j) { System.out.printf(%d*%d%d\t, j, i, i * j); } System.out.println(); } } }注意最后一行的System.out.println()它的作用是换行。如果你写成System.out.print(\n)效果也一样。很多Java初学者分不清print、printf、printlnprint不换行地输出字符串。printf按格式化字符串输出不换行和C语言的printf类似。println输出内容后换行。这三个是不同的方法不是同一种写法的手滑。如果只用println你就得自己拼字符串比如System.out.println(j * i (i * j));这样也能实现但没有printf直观。我建议你两个方法都掌握因为很多面试官就喜欢问你“println和printf的区别”。Java版的另一个常见问题是字符串拼接里忘记括号。例如System.out.println(j * i i * j);由于运算符优先级*会先算所以i * j结果是乘积没问题。但如果我把和*混在一起比如System.out.println(j i * j);那结果就是整数加法不是拼接。遇到这种问题最简单的方法就是把要展示的结果用一个变量先存起来再拼接避免被优先级坑到。2.3 Python版end参数与f-stringPython的代码是最短的但陷阱也不少for i in range(1, 10): for j in range(1, i 1): print(f{j}*{i}{i * j}, end\t) print()这里range(1, 10)会生成从1到9的整数注意右边界10是不包含的。所以外层 i 是1到9。内层range(1, i 1)表示从1到i刚好就是每一行的列数。如果写成range(1, i)那第9行就只有1到8少了9特别隐蔽。再说print的end参数。Python的print默认在输出末尾加换行符如果你直接写print(f{j}*{i}{i*j})那每个式子都会单独占一行完全不是表格的样子。解决办法就是设置end\t让每个式子之间用制表符分隔。内层循环结束后单独写一个print()利用默认的换行符实现换行。f-string是Python 3.6以后才有的特性写法很简洁f{j}*{i}{i * j}花括号里可以直接写变量或表达式。如果你的环境还是Python 3.5就得改用{0}*{1}{2}.format(j, i, i * j)。现在绝大多数机器都是3.8以上了直接用f-string没问题。2.4 JavaScript版从控制台到网页如果你在浏览器控制台调试可以这么写for (let i 1; i 9; i) { let line ; for (let j 1; j i; j) { line ${j}*${i}${i * j}\t; } console.log(line); }这里我用let line把每一行的内容先拼成一个字符串再一次性console.log输出。为什么不直接在内层console.log因为控制台每条log都会自动换行直接打印会得到一列表达式根本不是表格。拼接的好处是你可以完全控制换行时机。如果要在网页上显示这张表那就得操作DOMlet html ; for (let i 1; i 9; i) { for (let j 1; j i; j) { html ${j}*${i}${i * j} ; } html br; } document.getElementById(table).innerHTML html;在HTML里用空格分隔可能还不够清晰常见的做法是把每个式子放进span标签里设置等宽字体和间距。这里为了省事直接在字符串里加空格。这个版本稍后跟真实打印那一节联动能直接生成一张可以在浏览器里打印成A4纸的九九口诀表。3. 进阶玩法把口诀表玩出花来3.1 对齐的艺术让表格真正像表格用\t做分界很方便但不同终端、不同字体下制表符的宽度不一致输出的表格可能歪歪扭扭。想让表格漂亮就要用固定宽度占位符。以C语言为例printf(%d*%d%-3d, j, i, i * j);这个语句中的%-3d表示整数左对齐占3个字符宽度。如果数字只有1位就补两个空格如果数字是10、12这种两位数就占满3位不需要补空格。这样每个式子结束后的宽度基本一致整体效果比制表符稳定得多。对应的Python写法是for i in range(1, 10): for j in range(1, i 1): print(f{j}*{i}{i * j:4}, end) print(){i * j:4}表示把乘积左对齐占4个字符宽度。如果不足4位就在右侧补空格。运行结果里每一列的起始位置能对齐看起来就是一张整齐的表格。右对齐则用:4效果会是数字的个位对齐这两种风格在程序输出里都很常见。3.2 输出完整方阵而不是只有下三角有些题目不只要下三角而是要求输出完整的9x9方阵。这时候内层循环要固定从1到9外层循环也是1到9。问题是当列号大于行号时没有对应表达式必须补空格占位。C语言实现#include stdio.h int main(void) { for (int i 1; i 9; i) { for (int j 1; j 9; j) { if (j i) { printf(%d*%d%-4d, j, i, i * j); } else { printf(%-8s, ); } } printf(\n); } return 0; }这里的技巧是占位空格的宽度要跟正常输出一个东西的宽度相等否则表格会错位。我用了%-8s来输出一个空格并左对齐占8位就是为了抵消正常输出“1*11 ”所占的宽度。这个玩法看起来很麻烦但能帮你更深刻地理解“占位符”“宽度”这些概念。实际做题时如果题目没要求方阵老老实实输出下三角就够了别画蛇添足。3.3 倒序输出看似简单其实容易错还有一种常见变体第一行输出9个式子第二行输出8个……最后一行只输出1个。思路是把外层循环倒过来#include stdio.h int main(void) { for (int i 9; i 1; i--) { for (int j 1; j i; j) { printf(%d*%d%d\t, j, i, i * j); } printf(\n); } return 0; }这里最容易犯的错是把内层循环也写成for (int j i; j 1; j--)。这样做不是不对而是输出的顺序会变成“99、89、7*9……”每一行内的算式顺序反过来了。如果你只是想让行数递减但行内依然从1开始递增内层循环就不要动只改外层。这种题往往考的是你能不能准确区分行和列的独立控制很多老手也会在这上面马虎。3.4 网页版与真实打印九九口诀表上A4纸既然题目带“打印”我们就不能只聊控制台。九九口诀表也常被做成学习海报在浏览器里显示出来后用打印功能输出到纸上。一个最简单的HTML页面可以这样写!DOCTYPE html html langzh-CN head meta charsetUTF-8 title九九口诀表/title style body { font-family: Courier New, monospace; } media print { .no-print { display: none; } table { border-collapse: collapse; } td { padding: 4px 8px; border: 1px solid #ccc; } } /style /head body div idtable/div button classno-print onclickwindow.print()打印口诀表/button script let html table; for (let i 1; i 9; i) { html tr; for (let j 1; j i; j) { html td${j}*${i}${i * j}/td; } html /tr; } html /table; document.getElementById(table).innerHTML html; /script /body /html把这段代码存成.html文件用浏览器打开点“打印口诀表”按钮就能看到打印预览。这里用到了一个关键知识点打印样式要写在media print里面这样屏幕上可以只显示数据打印时却带表格边框。很多网页打印出来没有边框、内容错位十有八九是CSS里没写打印样式。4. 常见问题与排查实录4.1 输出全挤成一团数字全粘在一起这是新手翻车率最高的问题。比如C语言里写下printf(%d*%d%d, j, i, i * j);而没有加制表符或空格输出就会变成这样1*111*22 2*24……原因很好理解每条式子之间没有分隔符数据一个接一个地输出。解决办法就是在格式串末尾加上\t或者在每个式子后面打印一个空格。如果你同时调整了宽度占位符例如%-4d其实也自带右侧空格能起到分隔效果。记住一个口诀“不怕多打一个分隔符就怕少打一个换行符。”4.2 换行位置不对表格变成一条长龙如果你在Python里这样写for i in range(1, 10): for j in range(1, i 1): print(f{j}*{i}{i*j}, end)运行结果就是一行超长的字符串因为两个print都用了end没有换行。或者说你在内层循环里用了print()每个式子都单独成行。这些都是“换行时机”没掌握好。正确的做法是内层循环负责输出一行里的所有式子结束后立刻换行。在最直观的控制台程序里换行指令写在外层循环体的末尾。如果你把换行指令写在外层循环的开头那第一行前面会冒出一个空行而且最后一行的换行会缺失。这个细节在输出到文件的时候尤其明显。4.3 出现重复算式19和91同时出现有同学写完循环后发现表格里同时出现了“199”和“919”第一反应是代码写错了。其实如果你的循环是这样写的for (int i 1; i 9; i) { for (int j 1; j 9; j) { printf(%d*%d%d\t, j, i, i * j); } printf(\n); }那输出的就是完整方阵当然会有重复。因为当 i 为9时j 会从1到9于是“19”“29”……“99”全都有而当 i 为1时j 也是1到9于是“11”“21”“31”……也全都有。题目如果要求的是口诀表通常只需要下三角也就是j i这个边界条件。如果题目真的要求全方阵那就不是重复而是题意如此。做题前一定要看清楚是“九九乘法口诀表”还是“九九乘法表方阵”。这两个词在外观上区别很大我见过有人因为没看清要求最后被扣分。4.4 系统打印服务已关闭 / 浏览器打印闪退怎么处理标题里的“打印”如果是想让九九口诀表真的从打印机里出来那就会遇到和代码无关的坑。很多人辛辛苦苦写完HTML点打印按钮却发现浏览器没反应或者弹窗一闪就没了又或者提示“系统打印服务已关闭”。我整理了几种最容易碰到的情况现象可能原因排查与处理提示系统打印服务已关闭Windows的Print Spooler服务被停止按WinR输入services.msc找到Print Spooler重启该服务浏览器点击打印就闪退打印机驱动异常或浏览器设置损坏先把默认打印机改成“Microsoft Print to PDF”看能否进入预览能进入则说明驱动有问题网页打印没有边框、内容错位缺少打印样式在CSS里写media print给表格设置边框和页面边距打印预览空白表格使用了display: none或元素没有加载完检查JS是否在DOM加载后才填充数据别在head里直接执行DOM操作这些都是实际会碰到的问题。尤其是“系统打印服务已关闭”很多人的第一反应是重装驱动其实进服务管理器把它重新启动就行。不过也别高兴太早这个服务如果频繁自动停止往往是打印机驱动冲突最好把不用的虚拟打印机都删掉。4.5 程序员口中的“打印”不止一种在写九九口诀表的时候我们说的“打印”是输出到控制台。但在实际开发中你还经常听到“打印日志”“打印SQL”“打印调试信息”这些说法它们本质上也是“把信息输出到某个地方”可能是控制台、日志文件或者调试窗口。比如项目里配置MyBatis打印SQL就是让框架把执行的SQL语句输出到控制台Visual Studio里把调试信息保存到日志文件并同时打印显示也是类似思路。所以不用把“打印”这个词想得太狭窄。它既可能是控制台输出也可能是把网页渲染成PDF还可能是日志系统里的一条记录。只要理解了“把数据变成可见文本”这个本质你在不同场景里都能快速迁移。回到九九口诀表这个题它的核心价值其实是让你学会用最朴素的方式理解输出机制无论将来做后端日志还是前端页面都能用上。我个人实际写这段代码时体会最深的一点是九九乘法表几乎是“输出控制”的最小完整样本。项目再复杂也逃不开“循环—拼接—格式化—输出—换行”这套动作。你可以把每一行看成一条日志记录把每个式子看成一个数据字段就能明白为什么很多人说“会九九乘法表就摸到了编程输出的门槛”。最后再分享一个小技巧几乎每种主流语言都支持类似C语言里的printf格式化占位符。你只要掌握了%d、%s、%-3d、\t这几个基础写法不管以后用Java的System.out.printf还是Python的f-string控制宽度思路都是相通的。遇到输出不对齐的问题先想想是不是宽度占位符少了或者换行位置错了。这两个原因能解决九成以上和“打印”有关的代码问题。
返回列表