笔试的输入输出

1,825 阅读6分钟

一、背景

平常做Leecode可能写习惯了,可能有的小伙伴还不习惯做牛客上的空文本的输入输出,有的时候算法搞出来了,却卡在了数据的输入输出上,导致代码不能AC。下面我总结了一些常用的数据输入输出格式,仅供参考! 注:基于C/C++编程语言

二、数据输入

下面是一个华为笔试编程题为例:

Q:老师想知道从某某同学当中,分数最高的是多少,现在请你编程模拟老师的询问。当然,老师有时候需要更新某位同学的成绩.

输入包括多组测试数据。 每组输入第一行是两个正整数N和M(0 < N <= 30000,0 < M < 5000),分别代表学生的数目和操作的数目。 学生ID编号从1编到N。 第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩 接下来又M行,每一行有一个字符C(只取‘Q’或‘U’),和两个正整数A,B,当C为'Q'的时候, 表示这是一条询问操作,他询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少 当C为‘U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。

输入例子:

5 7

1 2 3 4 5

Q 1 5

U 3 6

Q 3 4

Q 4 5

U 4 5

U 2 9

Q 1 5

咱先不研究这道题该怎么做,咱就说说这道题该怎么输入呢,上面给的例子只是一次的测试用例,我们的程序要保证可以通过很多很多这种格式的测试用例,首先是不是想到用while循环呢。

在说之前,首先了解一下比较常用的数据流读取函数:

1、scanf()函数

头文件:#include<stdio.h>**

**作用:**用户指定的格式从键盘上把数据输入到指定的变量之中。scanf函数返回成功读入的数据项数,读入数据时遇到了“文件结束”则返回EOF。

常用的形式一般都是数字、字符以及字符串的输入:

scanf("%d%c%s",&x,&y,z); //把数字输入到x,字符输入到y,字符串输入到z中

如果输入的时候数据之间有空格或者逗号怎么办

scanf("%d%d",&a,&b); //一个数据给a,空格给b
scanf("%d %d",&a,&b); //一个数据给a,第二个数据给b
//两个数据之间有逗号的情况
scanf("%d,%d",&a,&b); //一个数据给a,第二个数据给b

字符串数据怎么办:用sscanf()函数

sscanf函数原型为int sscanf(const char *str, const char *format, ...)。将参数str的字符串根据参数format字符串来转换并格式化数据,转换后的结果存于对应的参数内。具体功能如下:

  • 根据格式从字符串中提取数据。如从字符串中取出整数、浮点数和字符串等。

  • 取指定长度的字符串

  • 取到指定字符为止的字符串

  • 取仅包含指定字符集的字符串

  • 取到指定字符集为止的字符串

例如:

int main(){
    char s[15] = "123.432,432 11";
    int n;
    double f1;
    int f2;
    sscanf(s, "%lf,%d%n", &f1, &f2, &n);
    cout<<f1<<" "<<f2<<" "<<n;
    return 0;
} 
//输出结果
123.432 432 11

2、cin的使用

使用格式:

#include<iostream>

using namespace std;

首先要了解cin是输入输出流的一个对象,具体来说是istream的对象。主要形式: cin >> x,x可以是数字、字符或者字符串,如果是输入的是一个字符串,**cin使用空白、空格、制表符和换行符来确定字符串的结束位置。**例如:

输入字符串:Bronming is nice! 如果用cin输入,就只能得到Bronming,因为后面有空格,就代表一次输入结束了。

注意:cin和scanf()都是输入数据流,可以同时输入最后也可以同时输出。也就是说我可以先把数据输入到流中,然后程序会依次处理这些数据,而不用每处理一次都cin一下。

那么如何输入一个字符串呢?用getline()

3、getline()

**该函数读取一行数据,通过回车来确定输入的结尾!**常用的形式:

getline(cin,str);

看一种特殊的情况:混合输入字符串和数字怎么办

int number;
string name;
cin >> number;
getline(cin,name);
//此时出现的问题是,第一次cin输入结束后会留下一个回车符,然后getline看到回到符以后以为是遇到空行,然后就输入结束,导致名字无法输入。
//解决的办法:
int number;
string name;
cin >> number;
cin.get();  //先读取空行
getline(cin,name);

4、输入的另一种常用形式Stringsteam ss

头文件 #include<sstream>

sstream 主要用来进行数据类型转换,由于 <sstream> 使用 string 对象来代替字符数组(snprintf方式),就避免缓冲区溢出的危险;而且,因为传入参数和目标对象的类型会被自动推导出来,所以不存在错误的格式化符的问题。简单说,相比c库的数据类型转换而言,<sstream> 更加安全、自动和直接。

注意:流操作,和cin cout类似,只是一个是屏幕流一个是字符流。如果遇到空格就结束了,遇到字符就读一个字符,他是从cin继承来的,符合cin的特性。

在笔试中会经常碰到先读入一行字符串,然后提取里面的数字等操作。这时我们会首先想到sscanf()函数,还有一种方式就是用stringsteam ss。具体怎么操作呢?

int main(){
	string s = "1 23 # 4";
	stringstream ss;
    ss<<s;
    while(ss>>s){
        cout<<s<<endl;
        int val = convert<int>(s);
        cout<<val<<endl;
    }
    return 0;
}

//输出结果:1 1 23 23 # 0 4 4

再比如:

#include<iostream>
#include<string>
#include<sstream>//stringstream的头文件
using namespace std;
int main()
{
    string line;
    while(getline(cin,line))
    {
        int sum=0,x;
        stringstream ss(line);//复制line到stringstream ss
        while(ss>>x)//相当于输入一个个单词,会自动将一个个单词转化为数字
        {
            sum+=x;
        }
        cout<<sum<<endl;
    }
    return 0;
}

说明一下:定义完一个ss之后,把字符串传输到流有两种方式:

streamstring ss(str); //初始化传入

streamstring ss;
ss<<str; //操作符传入,你看箭头的方向,向着ss就是给ss传值

然后一行字符串就呆在ss里面了,这时候还是字符的形式,如果你想把字符转成数字的形式,就直接使用ss>>x就行了,注意x是数字格式的变量,也就是说ss把字符吐给了变量x,但是变量x就是我们想要的数字格式,好处就是完成了类型的自动转化。然后你就可以把数字转入到数组里面进行处理了。提醒一下:如果字符串中含有,等特殊字符,则需要转换成空格在进行上诉操作,或者使用sscanf()函数。

如果说是数字变成字符串:

int num = 126;
streamstring ss;
ss<<num; 
string str = ss.str();
cout << str << endl;

例如从文件读取数据,一行数据的格式为1,2,2,5,6:

void Read_Testdata() {
    int i=0;
    ifstream inFile;
    inFile.open(Testdata_name);
    if(!inFile.is_open()) {
        cout << "打开测试文件失败!!!" << endl;
        exit(0);
    }
    while(inFile) {
        string line;
        unsigned int id;
        getline(inFile,line); //读取一行数据
        //,转成空格
        for(unsigned int i = 0; i < line.length(); i++){
            if(line[i] == ',')
                line[i] = ' ';
        }
        stringstream ss(line); //定义一个stringstream类型的对象ss并用line进行初始化
        for(int j=0;j<3;j++)
        {
            ss >> id; //先把数据进行转换到unsigned_int类型,然后从流中读取数据到id中。
            num[i][j] = id;
        }
        i++;
    }
}

5、实际操作

既然你知道这么多输入函数,我想应该足够了。你现在再看看上面那个题,学会输入了吗?

#include<iostream>
using namespace std;
int main() {
	int N,M;
    while(cin>>N>>M) { //这不就把第一行的输入处理了吗
    	//看第二行数据是一个数组吗,而且题目说明输入的数字,所以可以一次把数据循环输入到一个数组里面去
        int ID[N];
        for(int i=0;i<N;i++) 
        	cin >> ID[i];
        //题目要求接下来又是M行,每一行包含一个字符和两个正整数,根据题目可知,我需要知道这个字符是多少,
        //需要知道这两个正整数是多少,所以我定义一个字符和两个数字变量。
        char ch;
        unsigned int a,b;
        for(int i=0;i<M;i++) {
        	//下面就是处理一行数据,注意这一行数据不是一个字符串,所以可以用
            cin >> ch >> a >> b;
            或者
            scanf("%c%d%d",&ch,&a,&b);
            //剩下的就是对这一行进行具体的操作了
            {}
        }
    
}

三、数据输出

数据输出就不用多说了,常用的就是printf("%d",a) 和cout << stri << endl;

还有就是一个sprintf函数,原型为 int sprintf(char *str, const char *format, ...)。作用是格式化字符串,具体功能如下所示:

  • 将数字变量转换为字符串。

  • 得到整型变量的16进制和8进制字符串。

  • 连接多个字符串。

      int main(){
        char str[256] = { 0 };
        int data = 1024;
        //将data转换为字符串
        sprintf(str,"%d",data);
        //获取data的十六进制
        sprintf(str,"0x%X",data);
        //获取data的八进制
        sprintf(str,"0%o",data);
        const char *s1 = "Hello";
        const char *s2 = "World";
        //连接字符串s1和s2
        sprintf(str,"%s %s",s1,s2);
        cout<<str<<endl; 
        return 0;
      } 
      
      一般常见的格式串:
      %% 印出百分比符号,不转换。
      %c 整数转成对应的 ASCII 字元。
      %d 整数转成十进位。
      %f 倍精确度数字转成浮点数。
      %o 整数转成八进位。
      %s 整数转成字符串。
      %x 整数转成小写十六进位。
      %X 整数转成大写十六进位。
      %n sscanf(str, "%d%n", &dig, &n),%n表示一共转换了多少位的字符
    

    四、实际练习

    光说不练假把式,咱还是以上面的华为真题为例: 这个程序并不难,就会老师问一次我回答一次,就是寻找数组的最大值,并且实时更新成绩。

      	#include<iostream>
    	using namespace std;
        int main(){
            int N,M;
            while(cin>>N>>M) { 
                int nums[N+1];
                for(int i=1;i<=N;i++) 
                    cin >> nums[i];
                char ch;
                unsigned int a,b;
                for(int i=0;i<M;i++) {
                   cin >> ch >> a >> b;
                   //更新数据
                   if(ch=='U') {
                       nums[a] = b;
                   }
                   //寻找最大值
                   else if(ch=='Q') {
                       if(a>b) swap(a,b);
                       int res=nums[a];
                       for(int i=a;i<=b;i++) {
                           res = max(res,nums[i]);
                       }
                       cout << res << endl; //输出部分
                   }
                 }
            }
            return 0;
        }
    

    我没有加详细注释,其实思路很简单,关键体现程序的输入输出! OK,代码通过

    五、总结

    以上就是最简单的输入输出,以后还会遇到其他的形式,我还会实时更新的,记得交流哦!