生成字符串

假设我们要创建多个名称连续的文件夹,名称由前缀加连续数字编号组成,如Icon1、Icon2等。显而易见,如果能将数字转换为字符串,我们就能利用字符串函数生成所需名称,这里是初次尝试。

#include <stdio.h>
#include <string.h>

int main()
{
    char name[64];  // 存储最终名称
    char num[2];    // C字符串以0结尾,因此这里最少需要2个空间。

    for (int i = 0; i < 10; i++)
    {
        num[0] = '0' + i; // 数字i转换对应的字符
        num[1] = 0;       // 字符串结尾'\0'标志

        strcpy(name, "Icon");
        strcat(name, num);

        printf("%s\n", name);
    }

    return 0;
}

由ASCII码表得知,字符'0'的ASCII码是48,故而数字索引i,与相应可显字符间的对应关系就是:'0' + i,即:

之后利用strcpy与strcat组成最终名称。

glimix.com

再灵活一些

我们希望这个功能更灵活些,它能根据指定的前缀名与数值区间定制名称,如Icon17, YF9999等。思付一下你会发现,之前的方式面对不确定的多位数时,有些蹩手。我们可以尝试编写一个数值到字符串的转换函数来解决这个问题。

#include <stdio.h>
#include <string.h>

void number_tostr(char *numstr, int num)
{
    // 函数只处理正整数
    assert(num >= 0);

    int index = 0;  // 字符串numstr位置索引
    int factor = 1; // 基数

    // 计算基数:个位数时(factor = 1),十位数时(factor = 10), ...
    int temp = num;
    while (temp /= 10)
        factor *= 10;

    printf("%d\t %d\t ", num, factor);

    // 填充字符串
    while (factor > 0)
    {
        // 假设此时num=123,factor=100
        numstr[index] = '0' + num / factor; // num/factor:相当于取最高位的值(123/100=1)
        num %= factor; // 高位值已经转换了,需要丢弃(num=123%100=23)
        factor /= 10;  // 已经转换了一位,因此基数需要缩小到下一阶(100/=10, factor这时为10)
        index++;       // 填充字符串下一位
    }

    // 此时index指示了字符串的结束位置,追加'\0'
    numstr[index] = 0;

    // 看看结果
    printf("\"%s\"\n", numstr);
}

int main()
{
    char buffer[64];

    printf("num\t factor\t string\n");
    printf("-----------------------------------\n");

    number_tostr(buffer, 0);
    number_tostr(buffer, 9);
    number_tostr(buffer, 10);
    number_tostr(buffer, 123);
    number_tostr(buffer, 2048);

    return 0;
}

glimix.com

函数number_tostr没有数组大小参数,这与字符串函数类似,需要由客户保证缓冲区足够长;同时也仅支持正整数。接下来我们完成自定义名称部分,首先移除掉number_tostr中相关的打印信息,然后增加make_name函数,

void make_name(char *name, const char *prefix, int num)
{
    char numstr[64];
    number_tostr(numstr, num);
    strcpy(name, prefix);
    strcat(name, numstr);
}

int main()
{
    char name[64];

    make_name(name, "hello", 9);
    printf("%s\n", name);

    make_name(name, "main", 99);
    printf("%s\n", name);

    make_name(name, "glimix_", 123);
    printf("%s\n", name);

    make_name(name, "com", 2018);
    printf("%s\n", name);

    return 0;
}

glimix.com

优先使用库函数

事实上,C语言运行时库提供了itoa()函数用于将数值转换为字符串,它由stdlib.h提供。

char* itoa(int value, char *string, int radix);

value是将要被转换的整数,string是转换后储存的字符数组,radix是转换进制数,如2, 8, 10, 16进制。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char strval[64];
    int num = 123;

    printf("num\t scale\t result\n");
    printf("%d\t %d\t %s\n", num, 2, itoa(123, strval, 2));
    printf("%d\t %d\t %s\n", num, 8, itoa(123, strval, 8));
    printf("%d\t %d\t %s\n", num, 10, itoa(123, strval, 10));
    printf("%d\t %d\t %s\n", num, 16, itoa(123, strval, 16));

    return 0;
}

glimix.com

显然,itoa()比我们自己实现的转换函数更加强大。当程序需要某些功能时,我们应该先查询库函数是否提供了实现;有,则优先使用库函数;现在再结合make_name()函数,可以更容易的生成所需名称了。

格式化

如果仅仅是要输出符合要求的名称,使用 printf("Icon%d", value); 之类的语句即可达到要求。很多时候 ,我们则是需要将格式化的信息输出到缓冲区(字符数组)中,库函数sprintf()可以帮助我们。

#include <stdio.h>
#include <string.h>
#include <direct.h>

int main()
{
    char name[64];
    int nums[4] = { 1, 12, 123, 1234 };

    for (int i = 0; i < 4; i++)
    {
        sprintf(name, "Icon_%03d", nums[i]);
        printf("%s\n", name);
        mkdir(name);
    }

    return 0;
}

sprintf()函数会将格式化后的内容写入到由第1个参数指定的字符数组中,即这里的name。与printf()一致,%d格式符用于指示输出整型数值,%03d格式符指示整型数值输出宽度为3位,不足3位时前面被0,超过3位时,则原样输出;mkdir()函数根据传递的路径创建一个目录,这里我们只传递了一个名称,因此会被认为是在当前工作目录下(即项目所在目录)创建相应的子目录。

glimix.com

glimix.com

陕ICP备2025078817号-1 陕公网安备61011202001108号