概要
完成一個程序,作用是統計一個文件夾下面所有文件的代碼行數。輸入是一個文件夾的絕對路徑,輸出是代碼行數。所以此程序的新特點有兩個:
- 統計某一文件夾下的所有文件;
- 可以任意指定本機硬盤上任何位置的某一個文件夾。
前言
在上一篇隨筆中熟悉了文件的基本操作。但仍然有改進的余地:統計特定文件時,還是需要手動輸入文件名。如果文件數量很多怎么辦?可不可以直接統計某個文件夾下面的所有文件的代碼行數?今天解決的就是這個問題。
思考
我們已經解決了一個問題:寫一個帶有文件操作的C語言程序,輸入文件名,輸出此文件下的換行數。
考慮了一下現有的成果以及新增的目標,有需要更改的地方應該是輸入,由文件名換成一個文件夾,然后通過程序掃描一遍這個文件夾下面所有的文件。然后讀取所有的文件名,存放到數組里。再把字符串由fopen一個個調用,統計行數。細節差不多是這樣,但總歸要先掃描文件夾吧!
怎么掃描文件夾下面的文件?
搜索引擎搜索:C語言怎么掃描文件目錄?
有找到幾個鏈接,比較有用,可以參考:
比較可惜的是,上述網站提供的參考方法有使用函數opendir,查找了一下,發現這是一個在Linux系統下的函數。本人沒有用過Linux。想了想,並沒有直接開始搜索“安裝Linux教程”,我心想:總有不用這個函數的方法吧!於是依舊在搜索引擎上不斷地尋找。
找了接近一小時也無果,我打算換個問法。於是我在搜索引擎打入了:統計一個文件夾下文件
立刻映入我眼簾的就有一個黑科技,我驚呆了。
我立刻在桌面按照步驟進行實驗。不得不說,看到結果,我非常滿意。這不就我要的文件夾下面的所有文件名的字符串組合嘛!
立刻通過編程實現這一功能,效果拔群。
#include<stdio.h>
int main()
{
FILE *fp;
fp = fopen("TEXT.bat","w");
fputs("DIR *.* /B> LIST.TXT",fp);
fclose(fp);
return 0;
}
這里面有一行神秘的操作碼,
DIR *.* /B> LIST.TXT
問題來了,這一行代碼到底是什么呢?還是搜索引擎幫助你:)
大概知道了:
-
dir 顯示目錄中的文件和子目錄列表;
-
/B 使用空格式(沒有標題信息或摘要);
-
* . * (兩個星號中間一個句點)就是說顯示所有文件,文件名和擴展名沒有限制。舉例說明:如果是*.txt,則是顯示所有擴展名為txt的文件;
-
最后面是字符串,很容易就知道是重定向輸出的文件名。
好了。那么進行下一步的操作。我們考慮到一個文件夾只統計C語言代碼的行數,所以這里我考慮只對.c后綴的文件進行這種操作。
#include<stdio.h>
int main()
{
FILE *fp;
fp = fopen("Text.bat","w");
fputs("DIR *.c /B> list.txt",fp);
fclose(fp);
fp=fopen("Text.bat","r");
fclose(fp);
return 0;
}
這兒遇到了第一個問題,運行可執行文件main.exe之后,文件夾內的文件情況如下圖:
沒有產生預期想要獲得的文本文件list.txt。看來問題出在:
fp = fopen("Text1.bat","r");
這行語句上。因為,已經正常生成.bat文件,並且雙擊運行的話還是能生成文本文件,說明.bat文件沒有問題,應該是打開文件的方式有問題。
立刻搜索:c語言打開bat文件。得到解決方法:
在程序中使用system() 函數。
假設bat文件的名稱叫a.bat 即:
system("a.bat");
通過搜素來的方法,輕松解決這個問題。
#include<stdio.h>
int main()
{
FILE *fp;
fp = fopen("Text.bat","w");
fputs("DIR *.c /B> list.txt",fp);
fclose(fp);
system("Text.bat");
return 0;
}
編譯后運行結果:
得到了我們想要的list.txt。也就是說.bat文件被正確地執行了。生成LIST的工作已經完成。現在已經有了存儲文件名字符串的文本文件,下面只需要從這些文件中讀取文件名,再調用上一篇隨筆中實現的功能,即可實現這次需要添加的新功能了。
實現的簡單的從list.txt中讀取文件名。打開文件進行行數統計:
#include<stdio.h>
#include<Windows.h>
#include<string.h>
int main()
{
FILE *fp;
fp = fopen("Text.bat","w");
fputs("DIR *.c /B>list.txt",fp);
fclose(fp);
system("Text.bat");
/*~~~the end of creating file name list~~~*/
/*~~~the beginning of get .c file name from list~~~*/
static int count = 0;
FILE *fp1, *fp2;
fp1 = fopen("list.txt","r");
char s[100], singleline[1000];
while(fgets(s, 100, fp1))//get the name(one line) of a file from the list
{
// int len=strlen(s);
// if(s[len-1]=='\n') s[len-1]='\0'; what is this?
printf("%s: ",s);
fp2 = fopen(s,"r");//open the correct file, according to the file name
while(fgets(singleline, 1000, fp2))
{
count++;
}
printf("%d\n", count);
fclose(fp2);
}
printf("\n");
fclose(fp1);
system("pause");
return 0;
}
我們先不要管帶有注釋 what is this? 的那一行代碼。
編譯后執行,這次我在可執行文件.exe的目錄下放了一些.c文件進行測試,看看運行結果如何:
我覺得代碼意思都很明了了,然而執行結果還是失敗的。
仔細觀察結果,發現幾個奇怪的點。
- .c文件是沒問題的,統計代碼行數的幾行代碼是由上一個版本引進的所以也應該是沒問題的,但是結果卻是0;
- 輸出中有多余的空行。
找到問題所在了!由於fgets函數碰到'\n'是會停止輸入的,\n留在了緩沖區,下一次fgets時會導致文件讀取失敗(個人猜想,可能有誤)所以我們要設法處理掉這個'\n'。
思考一下,數組s是存儲文件名的。舉"C++.txt"為例,假設造成讀入失敗的原因是字符串后面跟了一個\n
C | + | + | . | t | x | t | \n | \0 |
---|
'\n'的位置是s[strlen(s)-1]為此我們通過如下代碼進行debug
int len=strlen(s);
if(s[len-1]=='\n')
s[len-1]='\0';
再次編譯運行,完成功能。此時距離成功已經很近了!
如何指定任意的文件夾路徑?絕對路徑!
絕對路徑是什么?請看下面的代碼:
FILE * fp;
fp = fopen("test.txt", "r");
我們知道fopen函數的第一個參數是一個字符串,代表文件名,這里的"test.txt"就不是絕對路徑,而是當前文件夾下(也可以理解為.exe可執行程序的文件夾)的文件。如果要使用絕對路徑名,就把絕對路徑輸入進第一個參數即可。怎么看文件夾or文件的絕對路徑呢?
很簡單,鼠標右鍵,屬性,就可以看了。例如上圖test的文件夾的絕對路徑是E:\test
這里有一個注意點,在代碼中操作字符串時,要想代表輸入一個反斜杠,需要輸入兩個反斜杠。大家還記得轉義字符嗎?還有就是注意輸入時要把中文輸入法關掉了。
貼上比較粗略的代碼,附贈注釋,以及執行效果圖一張:
#include<stdio.h>
#include<Windows.h>
#include<string.h>
int main()
{
char filepath[1000], batpath[1010]; //the absolute path of a file folder and a .bat file.
gets(filepath);//input absolute path of a file folder
strcpy(batpath, filepath);
strcat(batpath, "\\Text1.bat");
FILE *fp;
fp = fopen(batpath, "w");
fputs("DIR *.c /B>list.txt", fp);
fclose(fp);
system(batpath);
/*~~~the end of creating file name list~~~*/
/*~~~the beginning of get .c file name from list~~~*/
static int count = 0;
FILE *fp1, *fp2;
fp1 = fopen("list.txt", "r");
char s[100];
char singleline[1000];
while(fgets(s, 100, fp1))//get one line from list, each line refers to a .c file name
{
int len = strlen(s);
if(s[len-1] == '\n') s[len-1] = '\0';
printf("%s: ", s);
fp2 = fopen(s, "r");
/*~~~the beginning of counting lines of code~~~*/
while(fgets(singleline, 1000, fp2))//open the correct file, according to the file name
{
count++;
}
printf("%d\n", count);
fclose(fp2);
}
printf("\n");
fclose(fp1);
system("pause");
return 0;
}
由圖片可以看出,我們想要的功能:輸入一個文件夾的路徑,統計文件夾下面的.c文件的代碼行數,已經實現了。
主要的工作實現原理都在上面這段代碼里,最后要做的工作就是
- 封裝;
- 實現可循環輸入;
- 錯誤信息提示;
- 考慮改改變量名、增加一點宏定義什么的,讓代碼更具有可讀性;
- 還可以考慮添加的功能,例如可以分別顯示每個文件各自的代碼行數,最后顯示一個總行數。
稍后我會把完成版代碼推上我的github。
后記
想了想還是要感謝下棟哥的啟發式教學,讓我完成了一個小小的東西,雖然沒啥技術含量吧,但是也是自己獨立完成的,成就感還是有的。
這個問題我想了一早上,到了中午卡在絕對路徑那兒,結果頭暈暈的就去睡午覺了,醒來之后才解決的。233
最后,當然就是有什么錯誤,或者是有疑惑的地方都可以跟我說哦!