比赛中的读入情况

173 阅读4分钟

前言:本文章用于收集比赛中的各种读入数据的操作,使用最快的速度选择最佳的读入输出方式。帮助自己整理和积累一些读入操作,使得能快速的对读入的数据进行反应。

字符串读入问题

不含空格的字符串读入

对于一般的题目 , 可能包含一些问题。

假设:

3 2
asdja1
kjldfa1
fdsfjA

其中只包含小写字母 , 大写字母 , 数字的。我们可以直接使用std::cin >> s 来读入一个字符串 非常好操作 下面是读入的代码

C ++ 读入方式
std::cin >> n >> m;
for (int i = 0; i < n; i ++ ) {
        std::cin >> s;
        std::cout << s << '\n';
}

C 语言读入方式

int n , m;
char s[100];


scanf("%d%d\n" , &n , &m);

for (int i = 0; i < n; i ++ ) {
        scanf("%s" , s);
        printf("%s\n" , s);
}
效果

image.png

含空格的字符串读入

我们知道scanfcin 读入都会以 "tab(\t)" , "__ (空格)" , "\n(换行)" 标记为读入结束符(在读入不是以上三种字符的内容之后的结束符)

所以对于含空格或者\t 的字符串就存在读入不完全的情况

给出例子:

3 2
as d ja1
kj l dfa1
fdsfj   A

如何解决呢?

方案一:
char s[100];

scanf("%d%d\n" , &n , &m);         // 注意这里的 "\n" 表示输入必须要这个格式

for (int i = 0; i < n; i ++ ) {
        gets(s);

        printf("%s\n", s);
}

这里的scanf 结束之后会按回车然后结束输入 , 但是这个回车会被 gets 吃掉导致多读 所以需要scanf("%d%d\n") , 规定格式 ,让scanf 吃掉即可(scanf 吃掉之后不会影响前面的数据读入)

char s[100];
	
	scanf("%d%d" , &n , &m);

	getchar();							// 这样也可以
	for (int i = 0; i < n; i ++ ) {
		gets(s);

		printf("%s\n", s);

getchar() 吃掉 '\n'

对于有的OJ不支持gets 怎么办 ?

引入 getline 函数 , 是gets()的绝对的平替

getline 函数

  • istream& getline (istream& is, string& str, char delim)
  • istream& getline (istream& is, string& str)

对于这个函数

  • istream& is 是输入流 , 笔者使用它的应该是 cin
  • string& str 读入的目的字符串
  • char delim 以delim作为输入结束符 , 默认是 '\n'

在此举例以“#”结束

image.png

我们知道了如何读入含空格的字符串的函数了? 那么会不会出现吃掉‘\n’的情况呢?

答案:会

int main(){

	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	std::cout.tie(0);


	std::string s;

	std::cin >> n >> m;

	getchar();
	for (int i = 0; i < n; i ++ ){
		getline(std::cin , s);
		
		std::cout << s << '\n';

	}

	return 0;
}

image.png

真正的问题其实是同步流被关闭了!

那么我们来看看我们将同步流开启会出现什么?

int main(){

    // std::ios::sync_with_stdio(0);
    // std::cin.tie(0);
    // std::cout.tie(0);


    std::string s;

    std::cin >> n >> m;

    getchar();
    for (int i = 0; i < n; i ++ ){
            getline(std::cin , s);

            std::cout << s << '\n';

    }

    return 0;
}	

image.png 这样却是可以的!

考虑到效率问题 , 关闭掉同步流对于大的数据读入可能会TLE。所以说我们需要考虑一种比较通用的方法 那么只能使用scanf 函数读入 , 字符串的使用getline读入 。

std::string s;
int n , m;

scanf("%d%d\n" , &n , &m);

for (int i = 0; i < n; i ++ ){
    getline(std::cin , s);
    std::cout << s << '\n';
}

本人还是没有发现效率比较好且简洁的输入输出方式 , 希望我以后能学习到!

update:

关于我们关闭同步流 ,getchar() 就无法使用的问题,在一次与别人交流的过程中发现一个更好的方法

  • cin.get() 类似于C语言的getcahr() , 但是在关闭同步流后我们无法正常使用getchar()吸掉'\n',所以可以使用cin.get()。

  • cin.ignore() 清除读入的一个字符,也是可以关掉同步流。

  • 为什么关掉同步流不能使用C语言的读入输出呢? 因为cin 和 printf 不会运行在同一个线程上,于是输出乱序了。貌似,我了解的是因为C++ ,为了兼容C语言,会有一个缓冲区,然后关掉同步流会导致这个缓冲区有问题。我不太了解。反正记住一点 , 关掉同步流,就不能兼容C语言读入输出函数了,不然容易出错。

int n;

int main(){

    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    std::cin >> n;

    std::string s;
    std::cin.get();
    // std::cin.ignore()    也可以
    getline(std::cin , s);


    std::cout << s;

    return 0;
}

输入读到文件末尾

这个的话比较的简单 笔者给出两个方式

C语言方式的读入

int n;

while(scanf("%d" , &n) != EOF){

        printf("%d\n", n);

}

C++ 方式的读入

std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);

int n;

while(std::cin >> n){

        std::cout << n << '\n';

}

依靠换行结束输入形式(sstream)

有时候时候,比如做图论题的时候,给定一条链,不会给定读入结点数的

image.png

类似于这样 , 给定3条链。但是不会说这条链长度。

对于这种情况,我们选择使用 <sstram> 这个头文件中的 stringstream

   std::cin >> n >> m;
   
   std::string line;
   
   getline(cin , line);
   while (n -- ){
       getline(cin , line);
       stringstream ssm(line);
       
       int p = 0;
       std::vector<int> t;
       while (line >> p) t.push_back(p);
       for (auto x : t)
            std::cout << x << ' ';
   }
 }

将一行信息读入line ,再通过stringstream 过滤掉所有空格, 将数据分离出来