【9-17】这个程序与前两个练习类似,但更加一般化了,它允许调用程序把逗号放在大数的内部,去除多余的前导0以及提供一个浮动的美元符号等。
这个函数的操作类似于IBM370机器上的Edit和Mark指令。它的原型如下:
char *edit(char *pattern, char const *digits);
它的基本思路很简单,模式(pattern)就是一个图样,处理结果看上去应该向它的样子。数字字符串中的字符根据这个图样所提供的方式从左向右复制到模式字符串。数字字符串的第一位有效数字很重要,结果字符串中所有在第一位有效数字之前的字符都由一个“填充”字符代替,函数将返回一个指针,它所指向的位置正是第一位有效数字存储在结果字符串中的位置(调用程序可以根据这个返回指针,把一个浮动美元符号放在这个值左边的毗邻位置)。这个函数的输出结果就像支票上打印的那样--这个值左边所有的空白符由星号或其他字符填充。
在描述这个函数的详细处理过程之前,看一些这个操作的例子是很有帮助的,为了清晰起见,符号◌用于表示空格。结果字符串中带下划线的那个数字就是返回值指针所指想的字符(也就是第一个有效数字),如果结果字符串中不存在带下划线的字符,说明函数返回值是个NULL指针。
现在让我们来讨论这个函数的细节。函数的第一个参数就是模式,模式字符串的第一个字符就是“填充字符”。函数使数字字符串修改模式字符串中剩余的字符来产生结果字符串。在处理过程中,模式字符串将被修改,输出字符串不可能比原先的模式字符串更长,所以不存在溢出第一个参数的危险(因此不需要对此进行检查)。
模式是从左向右逐个字符进行处理的。每个位于填充字符串后面的字符的处理结果将是三中选一
a 保持原样不被修改
b 被一个数字字符串中的字符代替
c 被填充字符取代
数字字符串也是从左向右进行处理的,但它本身在处理过程中绝不会被修改,虽然它被称为“数字字符串”,但它也可以包含任何其他字符,如上面的例子之一所示,但是,数字字符串中的空格应该和0一样对待(它们的处理的结果相同)。函数必须保持一个“有效标识”,用于标识是否有任何有效数字从数字字符串复制到模式字符串。数字字符串中的前导空格和前导0并非有效字符,其余的字符都是有效字符。
如果模式字符串或数字字符串有一个是NULL,那就是个错误,在这种情况下,函数应该立即返回NULL。
下面这个表列出了所有需要的处理过程。列标题“signif”就是有效标识,“模式”和“数字”分别代表模式字符串和数字字符串的下一个字符。表的左边列出了所有可能出现的不同情况,表的右边描述了每种情况需要处理的过程,例如如果下一个模式字符是“#”,有效标识就设为假,数字字符串的下一个字符是‘0’,所以用一个填充字符代替模式字符串中的#字符,对有效标识不作修改。
这个题目的描述很复杂,线索一多,观众就容易理不出头绪,我觉得《C和指针》这本书最精华的部分就在这些练习题里面,真的很想知道作者Kenneth A.Reek如何想出的这些题目,作者作为罗切斯特理工大学的教授,一直教授C语言,应该是课程教学练习的积累吧。
这个问题中线索很多,需要写出主框架后再将这些条件逐步加进去,有些条件一开始不好理解,影响主结构,可以先不管,比如有效位标示这个,significance这个概念很有意思,这个题目对有效位进行了扩展,如果是非0的数字,则存在有效位,如果格式字符串中有!也标示有有效位,不管是不是数字,是其他字符也算是有效位,这样子的可以很容易的对其他进制进行扩展。
#define TRUE 1
#define FALSE 0
#define NUL '\0'
char *edit(char *pattern, char const *digits ) {
int digit, pat_char, fill, significance;
char *first_digit;
//校验格式字符串和数字字符串 并 提取出填充字符串并校验
if(pattern == NULL || digits == NULL || (fill = *pattern) == NUL) {
return NULL;
}
first_digit = NULL;
significance = FALSE;
//从格式字符串中逐个取出
while((pat_char = *++pattern) != NUL) {
switch (pat_char) {
case '#':
case '!':
//
if ((digit = *digits++) == NUL) {
*pattern = NUL;
return first_digit;
}
//空格替换为0
if(digit == ' ') {
digit = '0';
}
//判断是否是有效位,并指向第一个有效位
if(digit != '0' || pat_char == '!') {
if(!significance) {
first_digit = pattern;
}
significance = TRUE;
}
break;
default:
//其它字符不变
digit = pat_char;
break;
}
//如果有有效位,就用数字,否则就用填充符号填充
*pattern = significance ? digit : fill;
}
return first_digit;
}