6.3 string的常见用法详解
问题 A: 字符串处理(题库问题造成的答案错误 )
题目描述
读入两个字符串,字符串除了数字还可能包括 ‘—’、‘E’、‘e’、’.’,相加之后输出结果,如果是浮点型,要求用科学计数法表示(最多包含10个有效数字)。
输入
输入包含多组测试数据。
每组输入占两行,每行一个字符串,测试数据保证字符串的构成严格按照题目中的描述。
输出
输出两个数字相加的结果,每组输出占一行。
样例输入
34.56
2.45e2
样例输出
2.7956e2
思路
写了一个下午,实在不知道哪儿错了,ityanger的AC代码确实能AC,但是也有错误的地方(当输入1和1.245e-2时,输出是1.1245e0),不知道Codeup的测试点是在哪。
2020.4.2傍晚补充:我把题库买了,原来题库里判定的输出就是跳0的操作(输入1和1.245e-2,输出1.1245e0),醉了……我的代码有一个不足的地方就是大整数还是会输出科学计数法,所以第一块的内容还需要改一下(目前的第一块内容只处理了小数点太多自动输出科学计数法的问题),改好了我会更新的哈。
2020.4.2晚补充:代码已经更新了哈,我已经测试过了,所有题库给的数据输出都是正确的(除了跳0的那一部分,那部分是题库里数据有问题),当然,因为to_string和stringstream都有精度问题(体现在输入小数点后位数过多的情况,此时两者都存在精度问题,但是如果用科学计数法输入的话,to_string有精度问题而stringstream没有),所以,如果输入太大或者太小的数,无论是to_string还是stringstream转换出来的输出都有问题。
大家可以看一下这个坑爹的题库(左边是答案输出,右边是我的输出):
这是光标输出数据对应的两个输入(第三行是我程序的输出):
我的思路是定义double型的两个数,输入之后把他们的和转化为string类型(因为double型会自动识别科学计数法进行计算,所以就只需要对加法后的结果进行操作)。这里有三种情况:①小数点位数过多,double型自动输出科学计数法,但是默认的科学计数法是带正号和0的(比如样例的默认计数法输出是2.7956e+002),所以也要处理;②普通输出小数的情况需要处理使其输出科学计数法;③整数不用科学计数法输出。
我这里第二种情况修改成了如果是100就不输出e了(比如输入1和1.245e-2输出1.01245,并没有输出1.01245e0),虽然还是错的……(补充:看了以上题库的输出的话,应该是要求输出e0的)
另外,如果用C++11的to_string函数的话,转化的精度只有6位,如果想要精度更高,需要用stringstream来对double型数据进行转换。
在这里我把那份AC代码和我的代码都放上来吧,但是那份AC代码我没仔细看,想要看详细注释的可以看这篇博文:
codeup字符串处理problem A 1983
代码
- 能在CodeupAC的代码
#include<stdio.h>
int main()
{
char str1[50],str2[50];
long long s,s1,s2,ans;
int i,a1,a2,a,b,c,w,flag;
while(scanf("%s %s",str1,str2)!=EOF){
s1=s2=flag=b=c=a1=0;
for(i=0;str1[i];i++){
if(str1[i]=='-')
flag=1;
else if(str1[i]=='.')
c=1;
else if(str1[i]=='e'||str1[i]=='E'){
sscanf(str1+i+1,"%d",&b);
a1+=b;
break;
}
else{
s1=s1*10+str1[i]-'0';
a1-=c;
}
}
if(flag) s1=-s1;
flag=b=c=a2=0;
for(i=0;str2[i];i++){
if(str2[i]=='-')
flag=1;
else if(str2[i]=='.')
c=1;
else if(str2[i]=='e'||str2[i]=='E'){
sscanf(str2+i+1,"%d",&b);
a2+=b;
break;
}
else{
s2=s2*10+str2[i]-'0';
a2-=c;
}
}
if(flag) s2=-s2;
if(a1<a2)
for(;a1<a2;a2--)
s2*=10;
else if(a1>a2)
for(;a1>a2;a1--)
s1*=10;
a=a1;s=s1+s2;
if(!s){
printf("0\n");
continue;
}
while(a<0&&s%10==0){
s/=10;
a++;
}
if(a>=0){
printf("%lld",s);
for(i=0;i<a;i++)
printf("0");
printf("\n");
continue;
}
flag=0;
if(s<0){
s=-s;
flag=1;
}
ans=1;w=0;
while(ans<=s){
ans*=10;
w++;
}
if(ans>1){
ans/=10;
w--;
}
if(flag)
printf("-");
printf("%lld",s/ans);
if(ans>1)
printf(".%lld",s%ans);
printf("e%d\n",a+w);
}
return 0;
}
- 我的代码(已修改,除了跳0部分的数据输出,其他输出都是对的)
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<string>
#include<iostream>
#include<iomanip>
#include<sstream>
//#include<fstream>
using namespace std;
int main(){
double a, b;
while(cin>>a>>b){
stringstream s;//使用stringstream,控制转换为字符串后的有效数字能达到10位
s<<setprecision(10)<<a+b;
string str = s.str();
//判断是否是大整数//
bool integer = false;
//判断是否是整数,因为stringstream会自动科学计数法
//然而to_string虽然能完整保存整数,但是又有精度问题(小数点后一定只有6位,而且小数点位数过多to_string直接是0)
string sss = to_string(a+b);
int PointPosition = sss.find(".");//标记小数点的位置
int cntzero = 0;//数0的个数
for(int i=PointPosition+1;i<sss.length();i++) if(sss[i]=='0') cntzero++;
if(cntzero==sss.length()-1-PointPosition && sss[0]!='0' || str[0]=='0' && str.length()==1){
//保证不是因为精度问题而输出0.000000的情况,但又不能把本来就是0的情况排除
integer = true;//如果小数点之后全是0,就是大整数
sss.erase(PointPosition, sss.length()-PointPosition);//删除小数点和后面的0
}
///
//ofstream ofs;
//ofs.open("C:\\Users\\kykj\\Desktop\\aaaa.txt", ios::out|ios::app);
string temp = "e";//用于处理e及后面的数字
if(str.find("e") != string::npos && integer==false){//如果自动输出了科学计数法,且是小数
int pos = str.find("e");//记录e的位置
int pos2 = 0;//记录e后面第一个非0数的位置
bool flag = false;//记录是否是负号
string tmp;
for(int i=pos+1;i<str.length();i++){
if(str[i]=='-'){
flag = true;
continue;
}
if(str[i]!='0'){
pos2 = i;
break;
}
}
if(flag==true) str.erase(pos+2, pos2-(pos+2));
else str.erase(pos+1,pos2-(pos+1));
//ofs<<str<<endl;
cout<<str<<endl;
}
else if(str.find(".") != string::npos && integer==false){//如果相加之后是浮点型
bool flag = false;//默认不是负数
bool dot = false;
int pos = str.find(".");//记录小数点所在的位置
int ff = 0;//记录第一个非0数字出现的位置
str.erase(str.begin()+pos);//删除小数点
if(str[0]=='-'){
flag = true;//如果true,表明结果是个负数
str.erase(str.begin());//把负号先删了
}
for(int i=0;i<str.length();i++){
if(str[i]!='0'){
ff = i;
str.erase(0, i);//删除前面多余的0
break;
}
}
if(str.length()==1) dot = true;//只有一位有效数字
if(dot==false) str.insert(1, ".");//如果不止一位有效数字,则直接在第一个数后面插入小数点
if(flag==true){
ff = ff+1+1;//如果是负数,ff加上原本删去的负号和小数点
str.insert(0, "-");//放回负号
}
else ff = ff+1;//如果是非负数,则只加上原来的小数点即可
if(pos-ff>=0){
string num = to_string(pos-ff);
temp += num;
str += temp;
}
else{
temp += "-";
string num = to_string(abs(pos-ff));
temp += num;
str += temp;
}
//ofs<<str<<endl;
cout<<str<<endl;
}
else{//如果不是浮点型
//ofs<<sss<<endl;
cout<<sss<<endl;//这里输出to_string后的sss
}
//ofs.close();
}
return 0;
}
小结
C++的string用起来确实非常方便,尤其是插入和删除操作(感觉这两个是最常用的),和C的字符数组相比便捷了很多,而且在C++11里面也有to_string这种函数能直接把数值类型转换成字符串类型,但是好像字符串类型转数值类型没有封装好的函数,只能用atoi或者stringstream的方法?(大概)
详细方法可以看下面这篇博文:
C++中string转int
【2020.4.29补充】C++11里有直接的string转其他类型的函数,诸如stoi(string->int)、stod(string->double)、stoll(string->long long)等等。