字符串转换、字符测试


若要将字符串转换为数字,则可以使用atofatoiatolatoll等函数,这些函数都包括在 stdlib.h 中:

double    atof( const char* str );
int       atoi( const char *str );
long      atol( const char *str );
long long atoll( const char *str );

atofatoiatolatoll等函数 会搜寻字符串中可以转换的部份,直到遇到无法转换的字符,字符串开头可以使用正负号,例如"+100""-100"atof可以接受科学计数,例如"12.3e-5""123E+4",这几个函数若没有可转换的字符则返回 0,若是转换结果超出了返回类型的范围,返回值没有定义,也就是难以检查错误。

C99 有一系列转换字符串的函数,使用起来比较麻烦一些:

long               strtol( const char *restrict str, char **restrict str_end, int base );
long long          strtoll( const char *restrict str, char **restrict str_end, int base );

unsigned long      strtoul( const char *restrict str, char **restrict str_end,int base );
unsigned long long strtoull( const char *restrict str, char **restrict str_end,
                             int base );
float              strtof( const char *restrict str, char **restrict str_end );
double             strtod( const char *restrict str, char **restrict str_end );
long double        strtold( const char *restrict str, char **restrict str_end );

这几个函数的第一个参数都接受来源字符串;第二个参数在函数执行过后,会用来存储字符串中第一个无法解析为数字的字符地址,如果设定为NULL,会忽略这个参数;第三个参数用来指定基底,如果设定为 0,从字符串中自动侦测基底;函数若没有可转换的字符串,会返回 0。

因此最简单的转换情况就是当成atof的替代品:

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

int main(void) {
    printf("\"1010\"\t二进制:\t%ld\n", strtol("1010", NULL, 2));
    printf("\"12\"\t八进制:\t%ld\n", strtol("12", NULL, 8));
    printf("\"A\"\t十六进制:\t%ld\n", strtol("A", NULL, 16));
    printf("\"012\"\t自动基底:\t%ld\n", strtol("012", NULL, 0));
    printf("\"0xA\"\t自动基底:\t%ld\n", strtol("0xA", NULL, 0));
    printf("\"junk\"\t自动基底:\t%ld\n", strtol("junk", NULL, 0));

  return 0;
}

执行结果如下:

"1010"  二进制:        10
"12"    八进制:        10
"A"     十六进制:      10
"012"   自动基底:      10
"0xA"   自动基底:      10
"junk"  自动基底:      0

若是转换结果超出了返回类型的范围,会将定义在 errno.h 的errno设为ERANGE,并返回各自返回类型的最大可容许数值(最大值或最小值),因此,可借由检查errno来看看转换是否有误:

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

int main(void) {
    long i = strtol("99999999999999999999999999999999999999999999999999", NULL, 10);

    if(errno == ERANGE) {
        printf("超出转换函数范围");
        errno = 0;
    }
    else {
        printf("%d", i);
    }

    return 0;
}

由于第二个参数在函数执行过后,会用来指向字符串中第一个无法解析为数字的字符,因此若想连续解析一组数字,数字以某一标点符号区隔,可以如下,这需要认识更多指针的观念,你可以在后续学习过指针之后,再回头看看这个范例:

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

int main(void) {
    const char *p = "10,200,3000,-400000";
    char *end;
    for (long i = strtol(p, &end, 10); p != end; i = strtol(p, &end, 10)) {
        printf("\"%.*s\":", (int)(end - p), p);
        p = end + 1;  // 新的字符串起点
        if (errno == ERANGE){
            printf("转换超出范围");
            errno = 0;
        }

        printf("%ld\n", i);
    }
}

执行结果如下:

"10":10
"200":200
"3000":3000
"-400000":-400000

若要测试字符为数字、字母、大写、小写等等,可以使用ctype.h中的isxxxx()函数,例如:

isalnum(int c):是否为字母或数字
isalpha(int c):是否为字母
iscntrl(int c):是否为控制字符
isdigit(int c):是否为数字
islower(int c):是否为小写字母
isprint(int c):是否为打印字符
ispunct(int c):是否为标点符号
isspace(int c):是否为空白
isupper(int c):是否为大写字母
isxdigit(int c):是否为16进制数字
...

这些函数事实上是宏,可以查看 ctype.h 得知更多的isxxxx函数,ctype.h 中也包括了像是可以进行字母大小写转换的tolowertoupper等函数。


展开阅读全文