/*
这个程序与前面一个练习程序相似, 但它更为通用.
它按照一个指定的格式字符串对一个数字字符串进行格式化, 类似于许多BASIC 编译器所提供的"print using" 语句.
函数的原型应该如下:
int format( char *format_str, char const *digit_str );
digit_str 中的数字根据一开始在format_str 中找到的字符从右到左逐个复制到format_str 中.
注意被修改后的format_str 就是这个程序处理的结果.
当你完成时, 确定format_str 依然是以字符NUL 结尾的.
根据格式化过程中是否出现错误, 函数返回真或假.
格式字符串可以包含下列字符:
# 在两个字符串中都是从右向左进行操作. 格式字符串中每一个# 字符都被数字字符串中的下一个数字取代. 如果数字字符串用完, 格式字符串中所有剩余的# 字符由空白符代替(但存在例外, 请参见下面对小数点的讨论)
, 如果逗号左边至少有一位数字, 那么它就不作修改. 否则它就由空白符代替.
. 小数点始终作为小数点存在. 如果小数点左边没有一位数字, 那么小数点左边的那个位置以及右边直到有效数字为止的位置都用0 填充.
下面的例子说明了这个函数的一些调用结果. 符号* 用于表示空白.
格式字符串: 数字字符串: 结果格式字符串:
##### 12345 12345
##### 123 **123
##,### 1234 *1,234
##,### 123 ***123
##,### 1234567 34,567
#,###,###.## 123456789 1,234,567.89
#,###,###.## 1234567 ***12,345.67
#,###,###.## 123 ********1.23
#,###,###.## 1 ********0.01
#####.##### 1 ****0.00001
为了简化这个项目, 你可以假定格式字符串所提供的格式总是正确的. 最左边至少有一个# 符号, 小数点和逗号的右边至少也有一个# 符号. 而且逗号绝对不会出现在小数点的右边.
你需要检查的错误只有:
a. 数字字符串中的数字多于格式字符串中的# 符号.
b. 数字字符串为空.
发生这两种错误时, 函数返回假, 否则返回真. 如果数字字符串为空, 格式字符串在返回时应未作修改.
你如你使用指针而不是下标来解决问题, 你会学到更多东西.
提示:
开始时让两个指针分别指向格式字符串和数字字符串的末尾, 然后从右向左进行处理.
对于作为参数传递给函数的指针, 你必须保留它的值, 这样你就可以判断是否达到了这些字符串的左端.
*/
typedef enum { false, true } bool;
#include <stdio.h>
bool Is_Format( char *formatStr, char const *digitStr )
{
/*
* formatCnt: 记录格式字符串中字符# 个数
* digitCnt: 记录数字字符串中数字个数
*/
unsigned int formatCnt, digitCnt;
char *const formatStrMark = formatStr;
char const *const digitStrMark = digitStr;
if( digitStr == NULL )
{
return false;
}
/* formatStr 指向格式字符串末尾的NUL 字符 */
for( ; *formatStr != '\0'; ++formatStr )
{
;
}
/*
* 假定数字字符串格式总是正确的(全部都是数字)
* digitStr 指向数字字符串末尾的NUL 字符
* digitCnt 记录了数字字符串中数字个数
*/
for( digitCnt = 0; *digitStr != '\0'; ++digitStr )
{
++digitCnt;
}
/*
* tmp: 临时指针
* Is_PointAppeared: 布尔值
*/
char *tmp;
bool Is_PointAppeared;
/* 从右向左遍历格式字符串和数字字符串, 直到遍历到格式字符串的起始位置 */
for( --formatStr, --digitStr, formatCnt = 0; formatStr != formatStrMark - 1; --formatStr )
{
switch( *formatStr )
{
case '#':
/*
* 格式字符串当前字符为'#' 时, 如果数字字符串还没走到最左端, 复制对应位数字到格式字符串的当前字符位, 随后两个指针都前移一位
* 如果此时数字字符串走到了最左端, 说明该位格式字符串要进行填充, 应该填充'0' 还是空格呢?
* 此时就要检测格式字符串当前位左边是否存在小数点'.', 如果有小数点, 则填充 '0', 如果没有小数点, 则填充空格,
*
* 格式字符串的当前位不管是填充还是复制的, 最后formatCnt 都应该自增1
*/
if( digitStr != digitStrMark - 1 )
{
*formatStr = *digitStr--;
}
else
{
for( tmp = formatStr, Is_PointAppeared = false; tmp != formatStrMark - 1; --tmp )
{
if( *tmp == '.' )
{
Is_PointAppeared = true;
break;
}
}
Is_PointAppeared ? (*formatStr = '0'): (*formatStr = ' ');
}
++formatCnt;
break;
case ',':
/* 格式字符串当前字符为逗号时, 如果数字字符串走到了最左端, 逗号改空格, 如果没有走到最左端, 逗号保留 */
if( digitStr == digitStrMark -1 )
{
*formatStr = ' ';
}
break;
case '.':
/*
* 小数点出现后, 小数点肯定会保留, 需要关注的是小数点左边有没有一位数字
* 如果没有, 则小数点左边那个位置要用'0' 来填充
*
* 小数点左边没有数字时, 程序还要检测小数点左边是否是字符#
* 原因是当格式字符串中, 当前小数点左边还是小数点时, 我认为它依然是符合规范的, 应该原封不东地把两个小数点都保留
*
* 题目中已经说明格式字符串最左边一定有一个字符#, 所以当前字符为小数点时, 可以把formatStr大胆地前移一位并解引用, 而不用担心越界问题
* 填充0 后, formatStr 前移一位
*/
if( digitStr == digitStrMark -1 )
{
if( *( formatStr - 1 ) == '#' )
{
*--formatStr = '0';
++formatCnt;
}
}
break;
default:
/* 如果格式字符串格式正确, 不干了, 直接返回false */
return false;
}
}
/* 如果格式字符串中的字符# 个数不小于数字字符串中的数字个数, 函数返回真, 否则返回假 */
return formatCnt >= digitCnt ? true: false;
}
int main( void )
{
char *digitStr;
char formatStr_1[] = "#####";
digitStr = "12345";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_1, digitStr );
Is_Format( formatStr_1, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_1 ): printf( "结果字符串: %-20sfalse\n", formatStr_1 );
char formatStr_2[] = "#####";
digitStr = "123";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_2, digitStr );
Is_Format( formatStr_2, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_2 ): printf( "结果字符串: %-20sfalse\n", formatStr_2 );
char formatStr_3[] = "##,###";
digitStr = "1234";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_3, digitStr );
Is_Format( formatStr_3, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_3 ): printf( "结果字符串: %-20sfalse\n", formatStr_3 );
char formatStr_4[] = "##,###";
digitStr = "123";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_4, digitStr );
Is_Format( formatStr_4, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_4 ): printf( "结果字符串: %-20sfalse\n", formatStr_4 );
char formatStr_5[] = "##,###";
digitStr = "1234567";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_5, digitStr );
Is_Format( formatStr_5, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_5 ): printf( "结果字符串: %-20sfalse\n", formatStr_5 );
char formatStr_6[] = "#,###,###.##";
digitStr = "123456789";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_6, digitStr );
Is_Format( formatStr_6, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_6 ): printf( "结果字符串: %-20sfalse\n", formatStr_6 );
char formatStr_7[] = "#,###,###.##";
digitStr = "1234567";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_7, digitStr );
Is_Format( formatStr_7, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_7 ): printf( "结果字符串: %-20sfalse\n", formatStr_7 );
char formatStr_8[] = "#,###,###.##";
digitStr = "123";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_8, digitStr );
Is_Format( formatStr_8, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_8 ): printf( "结果字符串: %-20sfalse\n", formatStr_8 );
char formatStr_9[] = "#,###,###.##";
digitStr = "1";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_9, digitStr );
Is_Format( formatStr_9, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_9 ): printf( "结果字符串: %-20sfalse\n", formatStr_9 );
char formatStr_10[] = "#####.#####";
digitStr = "1";
printf( "格式字符串: %-20s 数字字符串: %-20s ", formatStr_10, digitStr );
Is_Format( formatStr_10, digitStr ) ? printf( "结果字符串: %-20strue\n", formatStr_10 ): printf( "结果字符串: %-20sfalse\n", formatStr_10 );
return 0;
}
程序运行结果: