自己动手写C语言格式化输出函数(二)

    上接《自己动手写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 +  10, 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]

 

 

你可能感兴趣的:(C语言)