上接《自己动手写C语言格式化输出函数(一)》 。
三、格式化字符及字符串。
1
//
宽字符串转换ANSI字符串。参数:ANSI字符串,宽字符串,转换字符数(0不转换)。
2
//
返回实际转换字符个数
3
static INT WStrToStr(LPSTR dst, LPCWSTR src, INT count)
4 {
5
return WideCharToMultiByte(CP_THREAD_ACP,
0, src, -
1,
6 dst, count >
0? count +
1:
0, NULL, NULL) -
1;
7 }
8
9
//
格式化字符。参数:缓冲区,格式记录。返回缓冲区尾偏移
10
static LPSTR FormatCharA(LPSTR buffer, FormatRec *rec)
11 {
12 INT len, spaces;
13 LPSTR p = buffer;
14
15
if (rec->type == TYPE_LONG)
16 {
17 len = WStrToStr(NULL, (LPCWSTR)rec->param,
0);
18
if (len ==
0) len =
sizeof(CHAR);
19 }
20
else len =
sizeof(CHAR);
21 spaces = rec->width - len;
22
if (rec->left == FALSE && spaces >
0)
23 {
24 memset(p, CHAR_SPACE, spaces);
25 p += spaces;
26 }
27
if (rec->type == TYPE_LONG)
28 {
29 WStrToStr(p, (LPCWSTR)rec->param, len);
30 p += len;
31 }
32
else *p ++ = *(LPCSTR)rec->param;
33
if (rec->left == TRUE && spaces >
0)
34 {
35 memset(p, CHAR_SPACE, spaces);
36 p += spaces;
37 }
38 rec->param += rec->type == TYPE_LONG? TS_WCHAR : TS_CHAR;
39
return p;
40 }
41
42
//
格式化字符串。参数:缓冲区,格式记录。返回缓冲区尾偏移
43
static LPSTR FormatStrA(LPSTR buffer, FormatRec *rec)
44 {
45 INT len, spaces;
46 LPSTR p = buffer;
47
48
if (rec->type == TYPE_LONG)
49 len = WStrToStr(NULL, *(LPCWSTR*)rec->param,
0);
50
else
51 len = lstrlenA(*(LPCSTR*)rec->param);
52
if (rec->precision >=
0 && len > rec->precision)
53 len = rec->precision;
54 spaces = rec->width - len;
55
if (rec->left == FALSE && spaces >
0)
56 {
57 memset(p, CHAR_SPACE, spaces);
58 p += spaces;
59 }
60
if (rec->type == TYPE_LONG)
61 WStrToStr(p, *(LPCWSTR*)rec->param, len);
62
else
63 memcpy(p, *(LPCSTR*)rec->param, len);
64 p += len;
65
if (rec->left == TRUE && spaces >
0)
66 {
67 memset(p, CHAR_SPACE, spaces);
68 p += spaces;
69 }
70 rec->param += TS_PTR;
71
return p;
72 }
如果不涉及宽字符,格式化字符和字符串是很简单的。
对于字符和字符串,"%lc"和"%ls"表示宽字符和宽字符串,其它类型精度全部视为默认值,即ANSI字符和ANSI字符串。
宽字符的转换是由WStrToStr函数来完成的,而WStrToStr又是调用的Windows API函数WideCharToMultiByte,
在格式化字符0时,C语言的printf和sprintf有所不同,前者是用空格替代的。例如:printf("%s%c456", "123", 0),显示出来是“123 456",而sprintf(s, "%s%c456", "123", 0)后,s="123",因此,sprintfA也就是s="123"。
四、格式化整型数。
1
//
格式化数字串。参数:缓冲区,格式记录,数字串,数字串长度。返回缓冲区尾偏移
2
static LPSTR FormatDigitsA(LPSTR buffer, FormatRec *rec, LPCSTR digits, INT len)
3 {
4 LPSTR p = buffer;
5 INT spaces;
6
7
if (rec->precision >=
0)
8 rec->zero = FALSE;
9 rec->precision -= len;
10
if (rec->precision <
0)
11 rec->precision =
0;
12 spaces = rec->width - len - rec->precision;
13
if (rec->negative)
14 {
15 spaces --;
16
if (rec->left || rec->zero)
17 *p ++ = (rec->negative == -
1? CHAR_NEG : CHAR_POS);
18 }
19
if (rec->left == FALSE)
20 {
21
if (spaces >
0)
22 {
23 memset(p, rec->zero? CHAR_ZERO : CHAR_SPACE, spaces);
24 p += spaces;
25 }
26
if (rec->negative && !rec->zero && !rec->decimals)
27 *p ++ = (rec->negative == -
1? CHAR_NEG : CHAR_POS);
28 }
29
if (rec->precision !=
0)
30 {
31 memset(p, CHAR_ZERO, rec->precision);
32 p += rec->precision;
33 }
34 memcpy(p, digits, len);
35 p += len;
36
if (rec->left == TRUE && spaces >
0)
37 {
38 memset(p, CHAR_SPACE, spaces);
39 p += spaces;
40 }
41
return p;
42 }
43
44
//
整型数转换为数字串。参数:数字串,整型数,是否无符号整数
45
46
static INT IntToDigits(LPSTR digits, LONG src, BOOL *isUnsigned)
47 {
48 ULONG v;
49 LPSTR p = digits + MAX_DIGITS_SIZE;
50
51
if (*isUnsigned == FALSE && src <
0) src = -src;
52
else *isUnsigned = TRUE;
53 v = (ULONG)src;
54
do
55 {
56 *(-- p) = (CHAR)(v %
10 +
'
0
');
57 v /=
10;
58 }
while (v);
59
return (INT)(MAX_DIGITS_SIZE - (p - digits));
60 }
61
62
static INT LLongToDigits(LPSTR digits, LLONG src, BOOL *isUnsigned)
63 {
64 ULLONG v;
65 LPSTR p = digits + MAX_DIGITS_SIZE;
66
67
if (*isUnsigned == FALSE && src <
0) src = -src;
68
else *isUnsigned = TRUE;
69 v = (ULLONG)src;
70
do
71 {
72 *(-- p) = (CHAR)(v %
10 +
'
0
');
73 v /=
10;
74 }
while (v);
75
return (INT)(MAX_DIGITS_SIZE - (p - digits));
76 }
77
78
static INT numSizes[] = {
sizeof(CHAR),
sizeof(SHORT),
sizeof(INT),
sizeof(LONG),
sizeof(LLONG)};
79
80
//
格式化整型数。参数:缓冲区,格式记录,是否无符号整数。返回缓冲区尾偏移
81
static LPSTR FormatIntA(LPSTR buffer, FormatRec *rec, BOOL isUnsigned)
82 {
83 ULONG value;
84 INT len;
85 CHAR digits[MAX_DIGITS_SIZE];
86
87
if (isUnsigned) rec->negative =
0;
88
if (numSizes[rec->type] <= TS_PTR)
89 {
90 value = *(PULONG)rec->param;
91
if (isUnsigned)
92 value &= ((ULONG)(-
1) >> ((TS_PTR - numSizes[rec->type]) <<
3));
93 len = IntToDigits(digits, value, &isUnsigned);
94 }
95
else
96 len = LLongToDigits(digits, *(PLLONG)rec->param, &isUnsigned);
97
if (!isUnsigned) rec->negative = -
1;
98 rec->param += TypeSize(numSizes[rec->type]);
99 rec->decimals =
0;
100
return FormatDigitsA(buffer, rec, &digits[MAX_DIGITS_SIZE - len], len);
101 }
在C的基本数据中,整型数的表达范围是最“与时俱进”的。16位编译器时,int是2字节,long为4字节;而32编译器下,int和long都变成了4字节,另外多了个8字节的_int64类型;64位编译器下,int仍然是4字节,long成了8字节,是否会有个16字节的_int128?我没用过64位编译器,不知道。代码中定义了一个LLONG类型,并写了2个整型数转换字符串函数,凡是小于或等于指针长度范围的整型数,使用IntToDigits函数,否则使用LLongToDigits函数。从表面看,这2个函数除数据类型不同外,语句是一样的,但编译后,前者的速度要快。如果是写商用的函数,建议还是使用插入汇编进行转换,因为汇编只作一个除法,就可的到商和余数,而高级语言需作2个除法。
有些C语言格式化输出函数在整型数转换时,是忽略hh(或者H)精度的,也就是说整型数转换的最小精度为sizeof(SHORT),而sprintfA的整型数的最小精度为sizeof(CHAR)。比如"%hhu", -123,前者输出是65413,而后者却是133。如果把代码中numSizes数组的第一个元素改为sizeof(SHORT),sprintfA也会忽略hh(或者H)精度。
五、整型数格式化为十六进制和八进制数字串。
1
static CHAR hexDigitsU[] =
"
0123456789ABCDEF
";
2
static CHAR hexDigitsL[] =
"
0123456789abcdef
";
3
4
//
整型数转换为十六进制串。参数:十六进制串,整型数,字节长度,转换精度,是否大写
5
static INT NumberToHexA(LPSTR hex, LPCVOID lpNumber, INT bytes, INT precision, BOOL upper)
6 {
7 LPSTR ph = hex;
8 LPBYTE pn = (LPBYTE)lpNumber;
9 LPSTR hexDigits;
10 INT len;
11
12
for (bytes --; bytes >
0 && pn[bytes] ==
0; bytes --);
13 pn += bytes;
14 bytes ++;
15 len = bytes *
2;
16
if ((*pn &
0xf0) ==
0) len --;
17
if (hex == NULL)
18
return precision > len? precision : len;
19
for (precision -= len; precision >
0; *ph ++ =
'
0
', precision --);
20 hexDigits = upper? hexDigitsU : hexDigitsL;
21
if ((*pn &
0xf0) ==
0)
22 {
23 *ph ++ = hexDigits[*pn -- &
0x0f];
24 bytes --;
25 }
26
for (; bytes >
0; bytes --, pn --)
27 {
28 *ph ++ = hexDigits[*pn >>
4];
29 *ph ++ = hexDigits[*pn &
0x0f];
30 }
31
return (INT)(ph - hex);
32 }
33
34
//
按十六进制格式化整型数。参数:缓冲区,格式记录,类型字符(x or X)
35
static LPSTR FormatHexA(LPSTR buffer, FormatRec *rec, CHAR hexChar)
36 {
37 LPSTR p = buffer;
38 INT spaces, len, pix;
39 BOOL upper = hexChar ==
'
X
';
40
if (rec->precision >=
0)
41 rec->zero = FALSE;
42 pix = rec->decimals?
2 :
0;
43 rec->precision -= pix;
44 len = NumberToHexA(NULL, rec->param, numSizes[rec->type], rec->precision, upper);
45 spaces = rec->width - len - pix;
46
if (rec->decimals && (rec->left || rec->zero))
47 {
48 memcpy(p, rec->decimals >
0? HEX_PREFIX_U : HEX_PREFIX_L,
2);
49 p +=
2;
50 }
51
if (rec->left == FALSE)
52 {
53
if (spaces >
0)
54 {
55 memset(p, rec->zero? CHAR_ZERO : CHAR_SPACE, spaces);
56 p += spaces;
57 }
58
if (rec->decimals && !rec->zero)
59 {
60 memcpy(p, rec->decimals >
0? HEX_PREFIX_U : HEX_PREFIX_L,
2);
61 p +=
2;
62 }
63 }
64 p += NumberToHexA(p, rec->param, numSizes[rec->type], rec->precision, upper);
65
if (rec->left == TRUE && spaces >
0)
66 {
67 memset(p, CHAR_SPACE, spaces);
68 p += spaces;
69 }
70 rec->param += TypeSize(numSizes[rec->type]);
71
return p;
72 }
73
74
//
整型数转换为八进制串。参数:八进制串,整型数,字节长度
75
static INT NumberToOtcalA(LPSTR otcal, LPCVOID lpNumber, INT bytes)
76 {
77 LPSTR p = otcal + MAX_DIGITS_SIZE;
78 ULLONG v =
0;
79 memcpy(&v, lpNumber, bytes);
80
do
81 {
82 *(-- p) = (CHAR)((v &
7) +
'
0
');
83 v >>=
3;
84 }
while (v);
85
return (INT)(MAX_DIGITS_SIZE - (p - otcal));
86 }
87
88
//
按八进制格式化整型数。参数:缓冲区,格式记录
89
static LPSTR FormatOctalA(LPSTR buffer, FormatRec *rec)
90 {
91 CHAR otcal[MAX_DIGITS_SIZE];
92 INT len = NumberToOtcalA(otcal, rec->param, numSizes[rec->type]);
93 rec->param += TypeSize(numSizes[rec->type]);
94 rec->negative =
0;
95
return FormatDigitsA(buffer, rec, &otcal[MAX_DIGITS_SIZE - len], len);
96 }
整型数转换为十六进制或者八进制数字串,除了进制不同,其它与前面整型数转换为10进制数是一样的。
六、格式化指针。
1
//
按十六进制格式化指针。参数:缓冲区,格式记录
2
static LPSTR FormatPointerA(LPSTR buffer, FormatRec *rec)
3 {
4 INT prec = PTR_SIZE <<
1;
5 CHAR tmp[PTR_SIZE *
2];
6
7 NumberToHexA(tmp, rec->param, TS_PTR, prec, TRUE);
8 rec->precision = -
1;
//
忽略精度
9
return FormatDigitsA(buffer, rec, tmp, prec);
10 }
因为指针地址同样也是个整型数,所以指针的格式化和整型数转换为十六进制数字串是一样的,只不过精度是固定的,32位编译器下为8位十六进制数,64位编译器下则为16位十六进制数。
七、获取缓冲区当前位置字节数。
1
//
获取缓冲区当前位置字节数。参数:缓冲区,缓冲区首地址,格式记录
2
static VOID GetPosSizeA(LPSTR buffer, LPSTR buffer0, FormatRec *rec)
3 {
4 LLONG size = buffer - buffer0;
5 memcpy((LPVOID)*(PLONG*)rec->param, &size, numSizes[rec->type]);
6 rec->param += TS_PTR;
7 }
这是格式化输出函数中最特殊的输出,它不是把某个参数的值输出到缓冲区,而是把输出缓冲区当前位置的长度输出到某个参数,这个参数必须是指针形式的。
同整型数转换为数字串一样,sprintfA确认的最小数据精度为sizeof(CHAR),也可以改变为sizeof(SHORT)。
未完待续......
声明:本文代码主要供学习使用,如作其它用途,出问题慨不负责。
水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]