写一个程序,统计自己C语言共写了多少行代码,Github基本操作
前言
在上一篇博客中,本人提到了自己的文件操作可以说是几乎没用过。现在想想,这也算是只在OJ上做题的一个弊端吧。虽然通过OJ做题是一个学习代码好手段,但其他方面也要多多涉猎才好,而不是说OJ用不到文件操作,就不去使用了,要坚持贯彻Learning by doing(咳)。写完上一篇博文之后,偷懒了三天,今天我就花时间学习了上次的不足,也就是这第二篇博客的标题。
C语言的文件操作
在编码过程中,尤其是在初学时,控制台和命令行是我们打交道的对象。有时候学了很久的代码,打出来的程序也只能是和小黑框交互,从键盘输入再看结果。面对着黑框框,我们也许会有疑问,我们怎样做点更高级的事情?这不,跟文件的操作是我们必须要熟悉的。
下面回答一个问题:
学过了C语言,你明白文件和流的区别和联系吗?如何区分文本文件和二进制文件?如何编程操作这两种文件?
文件是什么?
文件(file)通常是磁盘上一段命名的存储区。比如stdio.h就是一个包含一些有用信息的文件的名称。对于操作系统来说文件就会更复杂一些……您需要考虑的是如何在C程序中处理文件。
——《C Primer Plus》P354
在计算机中,有很多计算机文件。是以计算机硬盘为载体的存储在计算机上的信息的集合。文件的类型很多,例如word文档、txt文件、图像、可执行程序等。文件通常有几个字母组成的扩展名,来指示文件类型。
什么是流?(stream)
接触C++,我们会知道有这么一行代码:
#include <iostream>
通过学习,我们会知道iostream是“输入输出流”的意思,也就是in(入)、out(出)、stream(流)三个单词的组合。我们知道有I\O,那么stream到底是个什么东西?
C++程序把输入和输出看做字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入流中。对于面向文本的程序,那么每个字节代表一个字符。输入流的字节可能来自键盘,也可能来自存储设备(如硬盘)或其他程序。同样,输出流中的字节可以流向屏幕、打印机、存储设备和其他输出设备或程序。流充当了程序和流源或目标之间的桥梁。——《C++ Primer Plus》P732
它们之间的联系是什么?
我觉得上面的这句话“流充当了程序和流源或目标之间的桥梁”讲得非常好,同时书上还举了一些比较具体的例子方便我理解,这里我就不详尽地复述了。
再通过搜素他人的博客(C++ 流(stream)总结)进行学习。
C++中把数据之间的传输操作称为流
以下是我个人的理解:几乎所有程序都需要输入和输出,也就需要数据的传递。比如一个程序从输入中获取数据,再发送一些数据给输出设备。传递着的数据,就像水流一样,所以用stream这个单词来描述很恰当,同时也很形象生动,中文的话就用流来简称。
举个例子,fgets函数,原型为
char *fgets(char *buf, int bufsize, FILE *stream);
第三个参数的名字就是stream,流(还记得好的变量名能够给人以提示的作用吗)。调用fgets函数时,第三个参数可以写stdin,也就是标准输入,通常是指键盘输入。当然也可以写一个FILE指针,就是从文件读入数据了。这里流的作用就是把这个输入的数据的去向和函数(程序)关联起来。可以把这种数据看做水一样,流就是数据的载体。

文本文件和二进制文件
可以参考这篇博客:文本文件和二进制文件的区别
下面是编程操作的过程!
写一个程序,统计代码的行数
方法一,比较简单,但没有文件操作是一个缺点,需要复制代码进小黑框看结果。但解决问题的基本思路可以在这个代码里看出来,就是统计\n的数量有多少。
#include<stdio.h>
int main()
{
char c;
int count = 0;
while ((c = getchar()) != EOF)
{
if (c == '\n')
count++;
}
printf("%d\n", count);
return 0;
}
下面介绍几个跟此任务有关的文件操作的知识:
1. FILE *fp是什么?
基本上写C语言的文件操作都有这么一行,但我想更深一步的知道这行语句的作用是什么,于是在搜索引擎中搜索FILE得到以下结果:
C语言中FILE在"stdio.h"中有如下定义
struct _iobuf {
char *_ptr; //文件输入的下一个位置
int _cnt; //当前缓冲区的相对位置
char *_base; //指基础位置(即是文件的其始位置)
int _flag; //文件标志
int _file; //文件的有效性验证
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; //缓冲区的大小
char *_tmpfname; //临时文件名
};
typedef struct _iobuf FILE;
观察代码之后就能比较容易的理解了。简单来说C语言中定义了一个结构体,所以说白了FILE也是一种变量类型。有了FILE * fp语句之后,fp是一个FILE 类型指针。很多跟文件有关操作的参数or返回值就是FILE * 类型的。
2. fopen
是一个比较重要的函数。 函数原型:
FILE * fopen(const char * path,const char * mode);
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno中。
第一个参数是要打开文件的文件名指针,一般用双引号括起来的文件名表示,也可使用双反斜杠隔开的路径名。第二个参数是指打开的方式 主要有以下几种:
| 模式字符串 | 意义 |
|---|---|
| "r" | 只读 |
| "w" | 指针指到文件头,只写。如果文件不存在会创建文件。用任意的"w"模式打开一个已有的文件,会清空其内容。 |
| "a" | 指针指到文件尾,也就是可以向已有文件的尾部追加内容。如果文件不存在会创建文件。 |
| "r+" | 打开一个已存在的文件,可以读or写 |
| "w+" | 打开一个已存在的文件,可以读or写,如果文件不存在会创建文件。如果已有则会清空内容。 |
| "a+" | 打开一个已存在的文件,可以读or写,向已有文件尾部追加内容。如果文件不存在会创建文件。可以读取整个文件,但写入时只能追加内容。 |
| "rb","wb","ab","rb+","wb+","ab+" | 与前面的模式相似,区别是:前面六个操作的对象是文本文件,而这六个是操作二进制文件。 |
具体用法举例:
FILE *fp;
fp = fopen("test.txt","r");
3. 如何从文本中读取字符?fgetc函数
int fgetc(FILE *stream);
意为从文件指针stream指向的文件中读取一个字符,读取一个字节后,光标位置后移一个字节。
这个函数的返回值,是返回所读取的一个字节。如果读到文件末尾或者读取出错时返回EOF。所以可以看出,这里使用while语句,可以从头到尾扫描一个文件的字符。
4.fclose
int fclose(FILE * stream);
函数关闭由指针fp指定的文件。注意:使用fclose()函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区。
返回值:成功关闭文件,返回0,否则返回EOF。
这个统计代码行数的任务,只需要用到上面提到的几个文件操作,就可以写出对文件进行统计的程序代码了。是不是很简单呢?
在dev C++中运行结果正常,附上结果图一张:

我平时喜欢用Microsoft Visual Studio 2015 Community。在此IDE下fopen函数有报错。信息如下:

错误 C4996 'fopen': This function or variable may be unsafe.Consider using fopen_s instead.
看一下fopen_s的函数原型:

什么是fopen_s? errno_t又是什么?
搜索引擎搜索fopen_s函数,出现如下的实例代码:
//This program opens two files.It uses
//fclose to close the first file and
//_fcloseall to close all remaining files.
#include<stdio.h>
FILE *stream, *stream2;
int main(void)
{
errno_t err;
//Open for read(will fail if file "crt_fopen_s.c" does not exist)
err = fopen_s(&stream, "crt_fopen_s.c", "r");
if (err == 0)
{
printf("The file 'crt_fopen_s.c'was opened\n");
}
else
{
printf("The file 'crt_fopen_s.c'was not opened\n");
}
//Open for write
err = fopen_s(&stream2, "data2", "w+");
if (err == 0)
{
printf("The file 'data2'was opened\n");
}
else
{
printf("The file 'data2'was not opened\n");
}
//Close stream if it is not NULL
if (stream)
{
err = fclose(stream);
if (err == 0)
{
printf("The file 'crt_fopen_s.c' was closed\n");
}
else
{
printf("The file 'crt_fopen_s.c' was not closed\n");
}
}
//All other files are closed:
int numclosed = _fcloseall();
printf("Number of files closed by _fcloseall:%u\n", numclosed);
}
阅读以上代码可以得知:
errno_t也是一种类型,基本上是用来判断文件的打开和关闭的成功与否,0为成功的打开或关闭了文件。
fopen_s的功能和fopen大致一样,区别有:返回值类型是errno_t而不是FILE * ;有三个参数;第一个参数类型是FILE **。
改进版本
根据以上信息可以比较轻松地完成debug,简单的替换函数即可。
如果要累计各个文件总共的代码行数只需在程序中把int count = 0; 这一语句改成
static int count = 0;
即可。最后的运行情况是:

结果也是正确的!
附上改进版本的github链接。
后记
关于对Github的使用的学习,这里我参考了学长schaepher的博客 ,在此深表感谢!!
任何有错误的地方,或者有疑惑都欢迎和我交流。
写一个程序,统计自己C语言共写了多少行代码,Github基本操作的更多相关文章
- 写一个程序,统计自己C语言共写了多少行代码。ver2.00
概要 完成一个程序,作用是统计一个文件夹下面所有文件的代码行数.输入是一个文件夹的绝对路径,输出是代码行数.所以此程序的新特点有两个: 统计某一文件夹下的所有文件: 可以任意指定本机硬盘上任何位置的某 ...
- 大一C语言学习笔记(11)---编程篇--写一个程序,可以获取从键盘上输入的的三个数,并能够判断是否可以以这三个数字作为边长来构成一个三角形,如果可以的话,输出此三角形的周长及面积,要求 0 bug;
考核内容: 写一个程序,可以获取从键盘上输入的的三个数,并能够判断是否可以以这三个数字作为边长来构成一个三角形,如果可以的话,输出此三角形的周长及面积: 答案: #include<stdio.h ...
- JAVA-集合作业-已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。采用List集合和随机数
第二题 已知有十六支男子足球队参加2008 北京奥运会.写一个程序,把这16 支球队随机分为4 个组.采用List集合和随机数 2008 北京奥运会男足参赛国家: 科特迪瓦,阿根廷,澳大利亚,塞尔维亚 ...
- 迅雷笔试题 (JAVA多线程)启动三个线程,分别打印A B C,现在写一个程序 循环打印ABCABCABC
题目:http://wenku.baidu.com/view/d66187aad1f34693daef3e8a.html 启动三个线程,分别打印A B C,现在写一个程序 循环打印ABCABCABC. ...
- 用PHP写一个最简单的解释器Part4(写一个最简单的脚本语言)
好吧!我承认我想标题党了.大家对解释器的吸引,绝对没有自己动手写一个脚本语言更有吸引力.不过如果看到标题过来的,可能也是 我承认,之前收藏的减肥视频,我都是这样对待他们的. 不过我还是相信很多程序猿o ...
- 用C语言写一个程序,得出当前系统的整形数字长(16位,32位,64位)等,不能使用sizeof()
#include <iostream>#include <cmath>using namespace std; int main(){ int num = -1; unsign ...
- 第1讲——用C++写一个程序
一.学习新知识 在学习C++之前学过C语言了,一些基础的就不bb了,进入正题. 来几个小程序练练手: [程序1] #include <iostream> //头文件 using names ...
- 写一个程序可以对两个字符串进行测试,得知第一个字符串是否包含在第二个字符串中。如字符串”PEN”包含在字符串“INDEPENDENT”中。
package lovo.test; import java.util.Scanner; public class Java { @param args public static void main ...
- 写一个程序,用于分析一个字符串中各个单词出现的频率,并将单词和它出现的频率输出显示。(单词之间用空格隔开,如“Hello World My First Unit Test”)
public class Test { public void index() { String strWords = "Hello World My First Unit Test&quo ...
随机推荐
- 《JS权威指南学习总结--4.9.3in和instanceof运算符》
内容要点: 一.in运算符 in运算符希望它的左操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象.如果右侧的对象拥有一个名为左操作数数值的属性名,那么表达式返回true. 例如: va ...
- Tomcat目录下文件详解
一.Tomcat背景 (转) 自从JSP发布之后,推出了各式各样的JSP引擎.Apache Group在完成GNUJSP1.0的开发以后,开始考虑在SUN的JSWDK基础上开发一个可以直接提供We ...
- hdu_2899_Strange fuction(三分查找)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2899 题意:让你解方程 题解:对于只有一个凸或者没有凸的图像,可以直接上三分解决. #include& ...
- beego: 获取request参数
beego提供了一套web开发的框架.但我们在开发过程中遇到了一些问题,现汇总如下. 测试1:测试只有keys数组的情况 func (this *TestController) Index() { k ...
- sscanf用法
sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源. 1. 常见用法. 1 2 3 char buf[512] ; sscanf(" ...
- wpf计时器
wpf应用程序间隔一段时间触发一个事件,代码如下: public MainWindow() { InitializeComponent(); DispatcherTimer timer = new D ...
- Got a packet bigger than ‘max_allowed_packet’ bytes的解决方法
在使用 longtext 类型执行数据录入时,有时会抛出这个异常,从字面理解就是当前包大小超过 mysql 系统设置的包大小无法执行操作. 解释一下包大小这个东西:简单来说就是mysql把当前执行的m ...
- 第一个python实例程序
#!/usr/bin/python2.7 import os ls = os.linesep fname = raw_input("fname:"); while True: if ...
- this的应用
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- 利用apache组件实现文件上传
实现文件上传需要引入: commons-fileupload-1.3.2.jar commons-io-2.5.jar commons-logging-1.2.jar <!DOCTYPE htm ...