张银峰的编程课堂

重构ASCII码表输出程序

分析

使用函数可以有序的组织程序结构,现在我们尝试用函数重构一下ASCII码表输出程序。对于改进后的版本,有如下功能需求:

  1. 表头输出:每个编码输出其16进制、10进制及字符显示。
  2. 输出列出可设定,最少1列最多3列。
  3. 保持输出的工整性。
  4. 程序结构清晰,易于阅读。

回顾之前的程序,我们输出了表头,然后是ASCII表的内容,这两部分内容可以划分到独立的函数中,基于需求2,这两个函数应该接收一个代表列数的整数值,它们的原型如下:

  • void print_header(int columns);
  • void print_ascii(int columns);

对于像回车、换行、制表位等特殊字符会导到ASCII码表输出时位置错乱,之前的实现中,我们将这些特殊控制字符空格化,这里依旧采取这种策略,更进一步将它也安排到了一个函数中,该函数接收一个编码值,并返回最终的输出字符,其原型如下:

  • int escape_check(int ch);

实现

特殊字符处理

函数escape_check接收当前被处理的ASCII码,判断它是否需要转换为空格显示,如果是,返回空格,否则返回字符本身。这里使用了段注释的方式描述了函数的相关信息,段注释将以/*开头*/结尾的多行文本解释为注解,显然段注释是不能嵌套的。

/*
    作用:检测是否需要将特殊字符转换为空格
    参数:ch---输入字符
    返回值:需要特化时返回空格,否则返回原字符
*/
int escape_check(int ch)
{
    if (ch == 8 || ch == 9 || ch == 10 || ch == 13)
        return 32;
    else
        return ch;
}

ZYF

输出表头

我们使用for循环语句输出表头,列之间有一个水平制表符间距,表头与ASCII表用短横线分隔。

/*
    作用:输出表头
    参数:columns---列数
    返回值:无
*/
void print_header(int columns)
{
    // 输出表头
    for (int i = 0; i < columns; i++)
    {
        printf("DEC\tHEX\tCHR");

        // 不是最后一列时输出列间距
        if (i != columns - 1)
            printf("\t");
    }
    printf("\n");

    // 输出表头分隔线
    for (int i = 0; i < columns; ++i)
    {
        printf("-------------------");
        if (i != columns - 1)
            printf("\t");
    }
    printf("\n");
}

输出ASCII码表

下面的代码展示了两种可能的方案:

  1. 使用一个for循环+多个if语句实现,缺陷就是每执行一次,循环就停顿一下。
  2. 优先判断列数,每个分支一个循环处理,程序执行的比较流畅。
void print_ascii(int columns)
{
    //============ 方案一
    for (int i = 0; i < 128; i += columns)
    {
        if (columns == 1)
        { /* 输出... */ }
        else if (columns == 2)
        { /* 输出... */ }
    }

    //============ 方案二
    if (columns == 1)
    {
        for (int i = 0; i < 128; i += columns)
        { /* 输出... */ }
    }
    else if (columns == 2)
    {
        for (int i = 0; i < 128; i += columns)
        { /* 输出... */ }
    }
}

下面是我们采用第1种方案的一种实现。

//=============================================================================
void print_ascii(int columns)
{
    for (int i = 0; i < 128; i += columns)
    {
        printf("%d\t0x%x\t%c", i, i, escape_check(i));

        if (columns >= 2)
            printf("\t%d\t0x%x\t%c", i + 1, i + 1, escape_check(i + 1));

        // 输出第3列并阻止输出大于127的值
        if (columns >= 3 && (i + 2) < 127)
            printf("\t%d\t0x%x\t%c", i + 2, i + 2, i + 2);

        printf("\n");
    }
}

//=============================================================================
int main()
{
    // 从输入或配置文件读取列数
    int columns = 3;

    // 保证列的有效性
    if (columns < 1) columns = 1;
    else if (columns > 3) columns = 3;

    print_header(columns);
    print_ascii(columns);

    return 0;
}

ZYF