NOIP学习小站
西安交通大学附属中学航天学校

字符串例题——P1957 口算练习题

通过本例题介绍sscanf和sprintf函数的使用方法。字符串的处理是应用程序的重要操作,也是竞赛的考点,在编程时需要灵活处理。

这是一道常规问题,但是由于每行中输入的数据个数不一致,数据的处理是一个难点。进一步分析问题可知,每行有3个数据时,第一个数据是字符;每行有2个数据时,第一个数据是整数。这里应该用按行输入字符串的方式输入每行的算式。

每行算式按照字符串输入后,该如何进一步处理呢?这里可以使用一个有意思的函数sscanf,sscanf和scanf都是格式化输入函数,不过sscanf是指定从某个字符串中“输入”数据。printf函数其实有返回值,就是输出的内容的长度:

#include<cstdio>
#include<cstring>
char str[110],one[20];
int main()
{
    int n,a,b,c,len;
    char op;
    scanf("%d\n",&n);
    for(int i=1;i<=n;i++){
        cin.getline(one,sizeof(one));  //使用cin.getline输入一行有空格的字符串

        //sscanf指定从某个字符串中“输入”数据
        if(one[0]>='a' && one[0]<='c'){
            sscanf(one,"%c%d%d",&op,&a,&b); 
        }else{
            sscanf(one,"%d%d",&a,&b);
        }

        //printf函数的返回值(输出内容的长度)保存到len变量中
        switch(op){
            case 'a':c=a+b;len=printf("%d+%d=%d",a,b,c);break;
            case 'b':c=a-b;len=printf("%d-%d=%d",a,b,c);break;
            case 'c':c=a*b;len=printf("%d*%d=%d",a,b,c);break;
        }
        printf("\n%d\n",len);
    }
    return 0;
}

与sscanf类似,sprintf和printf都是格式化输出函数,不过sprintf是将数据“输出”保存到字符串中。

#include<cstdio>
#include<cstring>
char str[110],one[20];
int main()
{
	int n,a,b,c;
	char op;
	scanf("%d\n",&n);
	for(int i=1;i<=n;i++){
		fgets(one,sizeof(one),stdin);  //使用fgets输入一行有空格的字符串(本题不用关心输入后末尾可能存在的\n)

		//sscanf指定从某个字符串中“输入”数据
		if(one[0]>='a' && one[0]<='c'){
			sscanf(one,"%c%d%d",&op,&a,&b); 
		}else{
			sscanf(one,"%d%d",&a,&b);
		}

		//sprintf将数据格式化“输出”保存到字符串中
		switch(op){
			case 'a':c=a+b;sprintf(str,"%d+%d=%d",a,b,c);break;
			case 'b':c=a-b;sprintf(str,"%d-%d=%d",a,b,c);break;
			case 'c':c=a*b;sprintf(str,"%d*%d=%d",a,b,c);break;
		}
		printf("%s\n%d\n",str,strlen(str));
	}
    return 0;
}

当然,也可以扫描字符串生成操作数\(a\)和\(b\),计算得出结果\(c\),使用数的拆分方法计算结果\(c\)的位数,不过程序会显得繁琐:

#include<cstdio>
#include<cstring>
char str[110],one[20];
int main()
{
	int n,a,b,c,la,lb,lc,j;
	char op;
	scanf("%d\n",&n);
	for(int i=1;i<=n;i++){
		cin.getline(one,sizeof(one));  //使用cin.getline输入一行有空格的字符串

		j = 0;	//j记录字符串中第1个整数的开始下标 
		if(one[0]>='a' && one[0]<='c') op = one[0],j = 2;
		
		//生成a并计算a的位数
		a = la = 0; 
		while(one[j]>='0' && one[j]<='9') a = a*10+one[j]-'0',j++,la++;
		j++;
		
		//生成b并计算b的位数
		b = lb = 0;
		while(one[j]>='0' && one[j]<='9') b = b*10+one[j]-'0',j++,lb++;
		
		switch(op){
			case 'a':c=a+b;printf("%d+%d=%d",a,b,c);break;
			case 'b':c=a-b;printf("%d-%d=%d",a,b,c);break;
			case 'c':c=a*b;printf("%d*%d=%d",a,b,c);break;
		}
		
		lc = 2;	//lc是c的位数+运算符+等于符号+结果是负数时的负号 
		if(c<0) c = -c,lc++;	//结果是负数,特殊处理 
		
		if(c==0) lc++;	//结果为0,特殊处理 
		else{
			while(c) lc++,c /= 10;	//计算c的位数 
		}
		
		printf("\n%d\n",la+lb+lc);
	}
    return 0;
}

如果使用string来处理本问题的字符串,可以使用字符串流stringstream来将数据输出到字符串流或者从字符串流中输入数据到变量中:

#include<iostream>
#include<sstream>
using namespace std;
int main()
{
	int n,a,b,c;
	cin>>n;
	char op;
	
	//前面输入了整数,后面循环体中要用getline按行输入string,这里用下面的语句来做特殊处理
	//否则第一次循环的getline获取到的是一个换行符
	string tmp = "\n";
	getline(cin,tmp);

	for(int i=1;i<=n;i++){
		string s,ans;
		getline(cin,s);
		
		//字符串流ss1中的内容初始化成字符串s 
		stringstream ss1(s);
		if(s[0]>='a' && s[0]<='c'){
			//从字符串流ss1中输入字符op和两个整数a和b 
			ss1>>op>>a>>b;
		}else{
			//从字符串流ss1中输入两个整数a和b 
			ss1>>a>>b;
		}
		
		//将算式输出到字符串流ss2中
		stringstream ss2; 
		ss2<<a;
		switch(op){
			case 'a':ss2<<"+";c = a+b; break;
			case 'b':ss2<<"-";c = a-b; break;
			case 'c':ss2<<"*";c = a*b; break;
		}
		ss2<<b<<"="<<c;
		//从字符串流ss2中输入一个string变量ans  
		ss2>>ans;
		cout<<ans<<endl<<ans.length()<<endl;
	}
	return 0;
}