C和C++基本输入输出小谈
Table of Contents
今天翻了一下 Wunderlist ,发现有一个任务拖了好久,所以很有必要完成了。
记录是这样的:"把scanf/getchar/getch/gets这些讲一讲 by Tanky", 具体原因记不清了,大体是 TankyWoo 说好多 ACM 新手在输入输出这块问题还是比较多, 所以希望我有时间总结一下,写一写。没想到拖了这么长时间,实在惭愧。
所以,本文的读者定位在初级/中级 ACMer 对语言的基本需求,我基本上没有 AC 过,但是作为一个过来人,我大体知道输入输出这一块会卡在什么地方。
以前写过类似总结性的笔记:C/C++输入输出流总结,C/C++表面上给程序员提供了丰富的输入输出接口,正如文章中所介绍的一大堆函数。 但是,实际上,开发过程中用到的很少。对比 C,C++ 提供的不同输入输出函数,个人倾向于使用 C 语言的 fopen, fprintf, … 而不是 C++ 的 fstream 类。 C 语言所提供的接口更灵活好用,而 C++ 的 ">>" "<<" 让本来很简单的语句变的不清晰和复杂。比如:
fprintf(fp, "This is a int %d, this is a float %f", int, float); // C out << "This is a int" << int << ", this is a float " << float << endl; // C++
通过 fprintf 我一口气就可以读出来,我要输出的字符串是什么,而 out 不行。我一直觉得 C++ 的输出输出是非常鸡肋的,限制性多又难以理解, 尤其是 flags 的开启与关闭。再如类似 width 和 precision,我更喜欢用 C 语言的 0.3f% 做格式化控制。
C++ 控制输入输出相对牛逼的一点是有 stringstream 类,功能强大,相对也比较好用。只是,略复杂。
ok,不再吐槽,回到正题。
如果挨个函数去解释的话,不知道这篇文章要写多长,也没有意义。最好的学习网站: www.cplusplus.com,很好的支持和跟进新标准。 编译器推荐gcc,IDE推荐CodeBlocks,IDE 的选择可以看 10个最佳的C/C++编译器和IDEs,Eclipse也挺好,但是太大了,不推荐。
不要觉得这些东西不重要,选择最标准支持的好的编译器和一个易于调试的IDE,对于一个新手来说是至关重要的。
千万不要再用 VC6.0了,标准支持太差。为什么不推荐 vs2010/vs2012? 不免费,重量级,微软太喜欢给程序员强加东西了。
下面说一下,我个人认为输出输出这块的迷惑点:
1. 流的分类
当年,这是相当迷惑我的一个地方。也就是所谓的"二进制流"和"文本流",即"text file" or "binary file",代码上的区别就是打开文件时的参数有没有'b'。
怎么理解呢?我们知道计算机中所有的文件无非都是01二进制存储的,所以,所谓的文本流也是二进制文件,单靠字面的 text file , binray file 的区分很容易误导的。其实所谓的 text file 和 binary file 就是人肉眼能不能直观的看懂内容,能看懂即 text file ,看不懂 binray file。 你看我的博客内容是 text file,而一个可执行文件,一个 dll 就是 binary file 了。这两种文件,打开都是用 fopen,只是传入的参数有没有'b',对应的操作函数区别也比较大:
text file: fprintf fscanf, fputs, fgets ... binary file: fread, fwrite, fseek, ftell, ...
ACM 的话,用前者比较多,但是实际应用中后者居多。以我个人的经历来说,text file我做项目基本上没有用过。个人特别喜欢 fwrite, fread, fseek … 这些函数,灵活性强,可控性强。它不会像 fscanf 使用考虑那么多,基本上这几个函数就可以满足我的文件操作需求了。
其实常用的函数也就这么几个,想 rewind 这种函数我从来没用过。
值得一提的是 C11 新填了一个参数 'x',作用是如果文件存在的话,打开文件失败。
The new C standard (C2011, which is not part of C++) adds a new standard subspecifier ("x"), that can be appended to any "w" specifier (to form "wx", "wbx", "w+x" or "w+bx"/"wb+x"). This subspecifier forces the function to fail if the file exists, instead of overwriting it.
2. 文件打开失败
相信很多人会遇到这个问题,一般三个原因:
- 相对/绝对路径,这应该是最多数的情况了。绝对路径是相对盘符/根目录的,比如
C:\\program files
,/usr/bin/
, 相对路径是相对工程和可执行文件的,取决于你真实运行的起始端; - 文件正在被占用,同一个程序,运行了两次;
- 权限,如果在 Unix-like 系统下,如果文件打开失败,首先去找这个原因;
- 路径中的转义字符,"\"、"/"、路径中的空格都是常见问题,遇到的多了就知道了;
建议在 fopen 之后加一个 if 判断,确保第一时间得到文件打开失败的信息。
3. 文件无内容
文件打开成功,也写入了内容,程序关闭后无内容。原因是可能没有 fclose。如果想在程序运行的时候实时的看到文件内容的变化,可以写入一次调用一次 fflush(),调试程序可以这么用。但是,工程中就不要频繁的 fflush 了,会影响运行效率。
4. feof
这块出问题比较多,但是只要仔细的看文档,就不会有问题了。很容易记反!
A non-zero value is returned in the case that the end-of-file indicator associated with the stream is set. Otherwise, zero is returned.
5. 字符串截断符 '\0'
看看我遇到过的坑吧:C++|'\0'引发的小故事
6. getchar/getch
getchar() 两个注意点:
- 读取一个字符并回现,但是只有接收到回车才会函数返回;
- 返回值是 int 而不是 char;
getch() 两个注意点:
- 不是标准C函数,不建议使用;
- 读取一个字符不回现,印象中会立刻返回,不用等到接收到回车;
7. 最后,基本的格式化标准
这是所有的基础,你需要知道 %d 表示的整形,%f 表示的是浮点型,%s是字符串,且只以'\0'作为结束符等。类型不匹配也是经常出现的一个问题, 在 C 中有的时候参数不匹配,编译不会报错,但是运行就直接崩掉了。
文件输入输出和控制台输入输出基本相同,fprintf 和 printf 对应等等,可以理解成控制台也是一个特殊的文件。
有几篇相关的文章,感兴趣可以去看看:
- sprintf_s和swprintf_s在Release和Debug不同模式表现不同 微软的 '_s' 就是真的安全了吗?
- C++ 深入探究 while(cin) 你知道 C++ 背着你做了哪些事吗?
- C++ setf 方法 揭秘所谓的 flags,不过是一些标志位而已!
- 深度探索C语言函数可变长参数 fprintf, fscanf 为什么可以变长参数 ?
总感觉一写起来都有很多零碎的东西可以说。但是真正想说的是,遇到问题要勤于思考,勤于查阅,解决之后要勤于总结。即便 C 语言只是你的 AC 工具, 我依旧建议你这么做。毕竟解决问题能力的培养才是重要的。