到目前为止,我已经读到了K&R书,第2版的第5章,从中我学到了C。我花了几天时间思考这个项目的解决方案。我写了两个或三个版本的同一个程序,事实是,我无法为这个练习想出更好的解决方案:
练习5-11.修改程序"恩塔布“和"德塔普”(作为第1章的练习编写)。若要作为参数接受制表符列表,请执行以下操作。如果没有参数,则使用默认选项卡设置。
我以为这是我写过的最好的一本。以下是这项工作的解决办法(第5章,Ex-5.11):
我想知道如何改进它,使它“更进一步?”或者如果我采取了正确的方法:
/*-
* detab.c - expand tabs to equivalent spaces
*
* The-C-Programming-Language Book 2nd Edition.
*
* Created by jr.chavez on 9/22/22.
*/
#include <stdio.h>
#include <stdlib.h>
#define DTAB_STOP 8 /* DEFAULT TAB STOP */
#define MAX_TAB_STOP 100
enum escapes { BACKSPACE = '\b', TAB = '\t', NEWLINE = '\n', RETURN = '\r' };
static int
getstops(char *cp)
{
int n;
n = 0;
while (*cp) {
if (*cp < '0' || *cp > '9')
return -1;
if (*cp >= '0' && *cp <= '9')
n = n * 10 + *cp - '0';
cp++;
}
return n;
}
int
main(int argc, char *argv[])
{
int ch;
int n, col; /* n, columns */
int nstops;
int tabstops[MAX_TAB_STOP];
nstops = argc - 1;
if (nstops > MAX_TAB_STOP) {
fprintf(stderr, "error: too many args.\n");
exit(EXIT_FAILURE);
}
for (int indx = 1; indx < argc; indx++) {
tabstops[indx - 1] = getstops(argv[indx]) - 1;
}
col = 0;
while ((ch = getchar()) != EOF) {
switch (ch) {
case TAB:
if (nstops == 0) {
while (col < DTAB_STOP) {
printf("\u25A0");
col++;
}
continue;
}
for (n = 0; n < nstops; n++)
if (col < tabstops[n])
break;
if (n == nstops) {
printf("\u25A0");
col++;
continue;
}
while (col < tabstops[n]) {
printf("\u25A0");
col++;
}
break;
case BACKSPACE:
if (col > 0)
--col;
putchar(BACKSPACE);
break;
case RETURN:
case NEWLINE:
putchar(ch);
col = 0;
continue;
default:
putchar(ch);
break;
}
}
if (ferror(stdin)) {
perror("stdin");
return EXIT_FAILURE;
}
return 0;
}我做了一些“测试”:
cat test.txt | ./bin/detab 4 9 29输入文件:
# # #
# # #
# # # # #
# # #输出:
#■■■■■■■■■■■■■■■■■■■■■■■■■■■■#■■#
#■■■■■■■■■■■■■■■■■■■■■■■■■■■■■#■■#
#■■■■■■■■■■■■■■■■■■■■■■■■■■■■#■#■■■#■#
#■■■#■■■■■■■■■■■■■■■■■■■■■■■■■■#这些是我使用展开命令得到的结果:(当然,我从一开始就没有想过结果是准确的--但现在我只想改进它。)
cat test.txt | expand -t 4,9,29产出:
# # #
# # #
# # # # #
# # #注意:
发布于 2022-09-23 17:20:50
如何改进它,使其“更进一步”?
getstops(char *cp) (应该是getstops(const char *cp) )不会检测到无效的转换。最好使用strtol()并测试可接受的范围。输入错误时出错。
getstops(argv[indx]) - 1;在getstops()返回INT_MIN时调用未定义的行为。显然,这不是一个预期的返回值,更好的做法是在计算之前测试有效的用户值范围。
而不是printf("\u25A0");,考虑#define BLACK_SQUARE "\u25A0"、fputs(BLACK_SQUARE, stdout);等。
// fprintf(stderr, "error: too many args.\n");
fprintf(stderr, "error: More than %d args.\n", MAX_TAB_STOP + 1);
// or
fprintf(stderr, "error: More than %d tab stops.\n", MAX_TAB_STOP);不错:if (ferror(stdin)) {
我可以看到自动工具使许多点击停止。
// #define MAX_TAB_STOP 100
#define MAX_TAB_STOP 4096更好的代码将简单地允许argc制表符。大小为int tabstops[]到argc。
if (*cp >= '0' && *cp <= '9')没有用。人们已经知道cp在范围内。
#include <stdlib.h>
// Parse for positive int
static int getstops(const char *cp) {
errno = 0;
char *endptr;
long val = strtol(cp, endptr, 0);
if (cp == endptr || *endptr) {
fprintf(stderr, "Non-numeric conversion <%s>\n", cp);
exit(EXIT_FAILURE); // or alternative handling
}
if (errno || val < 0 || val > INT_MAX) {
fprintf(stderr, "Invalid tab stop %ld>\n", val);
exit(EXIT_FAILURE); // or alternative handling
}
return (int) val;
}发布于 2022-09-23 13:25:03
代码大部分是可读的。
命令行可能包含标志,因此命令行处理应该检查它们。
可能有更多的错误条件是您没有检查的。
其中一些变量是优秀的,如tabstops,变量名(如n、col和cp )可以改进。
luser droog在你的最后一个问题中的另一个答案中包含了这一点。
函数main()大约有60行代码,取决于您正在使用的编辑器或IDE,这可能是太多的代码行。在编辑器中不能容纳一个屏幕的函数通常被认为太大了。维护一个您无法同时看到的函数变得非常困难。
函数main()太复杂(做得太多)。随着程序规模的增加,main()的使用应限于调用解析命令行的函数、调用为处理设置的函数、调用执行程序所需功能的函数以及调用函数以清理程序的主要部分。
这里也有一个叫做单一责任原则的编程原则。单一责任原则指出:
每个模块、类或函数都应该对软件提供的功能的单个部分负责,而该责任应该完全由该模块、类或函数封装。
在main()中至少有3种可能的功能。
exit(status)函数在main()函数中,没有理由调用exit()函数。只应在操作系统不执行的函数中调用退出函数。
而不是
exit(EXIT_FAILURE);代码应该是
return EXIT_FAILURE;https://codereview.stackexchange.com/questions/279914
复制相似问题