C++遍歷日誌log目錄,並提取資料進行分析
1 前言
我們經常在編寫軟體的時候,需要載入log檔案來記錄程式執行過程中可能會出現的bug,或者記錄一些重要的執行資訊。一旦一個目錄下生成很多log檔案後,實際上我們管理與分析還是需要費一些時間的。這其中就需要我們懂得怎樣讀取log檔案,怎樣遍歷目錄,怎樣解析資料等等一系列操作。
下面我們直接通過一個例項來了解各個部分是如何實現的,這個例項的要求如下:
①、D盤log目錄下有很多.log檔案,我們需要從每個檔案中提取資料(隱藏兩個任務:開啟讀取檔案;遍歷目錄);
②、我們需要從檔案中提取speed關鍵字後面對應的資料放入到Excel表格.csv檔案中。
2 程式實現
2.1 讀取檔案
以前從C開始我們就學習了使用fopen()開啟檔案,fread()讀取資訊,後來由於存在不安全性,又有了類似的fopen_s()相關定義。但是本次我們使用更上層一些的流操作來讀取檔案,該類方法定義在#include<fstream>
中,實現如下:
#include<fstream>
using namespace std;
void GetLog(const string& file)
{
//string file = "D:/log/1.log";// 放置log檔案的目錄
ifstream logFile(file); // 構造一個檔案流讀取物件
string str;
while (getline(logFile, str)) // 隔行讀入資料
{
// ... // 資料處理部分
str.clear(); // 重複載入,所以每次需要清空
}
logFile.close(); // 關閉檔案
}
上面的ifstream類是用來構造讀取檔案物件的類,對應還有構造輸出到檔案的物件的類ofstream。如果既存在讀入和存取,有fstream類,根據自己的需要定義對應的類,然後後面我們就可以像終端上流輸入輸出一樣處理載入的資料。
2.2 遍歷日誌目錄
有了上面的檔案讀取功能函式,下面我們來看看如何載入一個目錄下所有log檔案來進行讀取與分析。這裡主要用到#include<io.h>
標頭檔案中定義的_findfirst()和_findnext()函式介面來依次讀取所有檔案,以及構造結構體 _finddata_t物件來儲存目錄下檔案基本資訊。我們還是直接看程式:
#include<string> // 字串類
#include<io.h> // 遍歷操作
struct _finddata_t fileinfo; // 儲存檔案資訊的結構體物件
string file = "D:/log/"; // 放置log檔案的目錄
string strFile = file + "*.log";
/***遍歷目錄系統函式要求先嚐試尋找一個檔案,看是否存在***/
long handle;
if ((handle = _findfirst(strFile.c_str(), &fileinfo)) == -1L)
{
return 0; // 如果查詢log檔案失敗,直接返回
}
else
{
strFile = file + fileinfo.name;
GetLog(strFile); // 對第一個載入的檔案處理
/***一直遍歷,直到所有.log檔案得到載入與處理***/
while (!(_findnext(handle, &fileinfo)))
{
strFile = file + fileinfo.name;
GetLog(strFile); // 檔案處理
}
coutReport.close(); // 釋放檔案載入
_findclose(handle); // 釋放遍歷目錄的控制代碼
}
2.3 資料處理
我們的案例中是一個小小的處理要求,就是對log檔案下提取一下speed關鍵字後面記錄的資料。這裡提取資料並一一對所有字串處理的操作主要用到定義在#include<sstream>
中字串流操作的stringstream類。跟fstream中定義的一樣,istringstream類是從檔案讀入資料,而ostringstream類是將資料存入檔案,而stringstream類就是兩種操作都可以。程式如下:
#include<sstream> // 字串流操作
#include<string> // 字串類
#include<io.h> // 遍歷操作
using namespace std;// 標準庫名稱空間
const char* coutFile = "D:/log/output.csv"; // 輸出提取資料到csv檔案
// 上面隔行讀入的資料
while (getline(logFile, str))
{
stringstream strRead(str);
string oneWord;
while (strRead >> oneWord) // 一個個word載入進去
{
if (oneWord.compare("Speed") == 0) // 字串查詢
{
int result;
strRead >> result; // Speed後面的數字輸出到result中
cout << result << endl; // 寫入檔案,並且空行
}
}
str.clear(); // 重複載入,所以每次需要清空
}
3 完整參考程式
#include<iostream> // cin、cout
#include<fstream> // 包含檔案讀取類與方法
#include<sstream> // 字串流操作
#include<string> // 字串類
#include<io.h> // 遍歷操作
using namespace std;// 標準庫名稱空間
string file = "D:/log/"; // 放置log檔案的目錄
const char* coutFile = "D:/log/output.csv"; // 輸出提取資料到csv檔案
ofstream coutReport(coutFile); // 構建輸出結果檔案類物件
void GetInfo(const string& filePath)
{
ifstream logFile(filePath);
string str;
// 隔行讀入資料
while (getline(logFile, str))
{
stringstream strRead(str);
string oneWord;
while (strRead >> oneWord) // 一個個word載入進去
{
if (oneWord.compare("Speed") == 0)
{
int result;
strRead >> result; // Speed後面的數字輸出到result中
cout << result << endl; // 寫入檔案,並且空行
}
}
str.clear(); // 重複載入,所以每次需要清空
}
// 關閉檔案
logFile.close();
}
int main()
{
struct _finddata_t fileinfo;
string strFile = file + "*.log";
/***遍歷目錄系統函式要求先嚐試尋找一個檔案,看是否存在***/
long handle;
if ((handle = _findfirst(strFile.c_str(), &fileinfo)) == -1L)
{
return 0; // 如果查詢log檔案失敗,直接返回
}
else
{
strFile = file + fileinfo.name;
GetInfo(strFile); // 檔案處理
/***一直遍歷,直到所有.log檔案得到載入與處理***/
while (!(_findnext(handle, &fileinfo)))
{
strFile = file + fileinfo.name;
GetInfo(strFile); // 檔案處理
}
coutReport.close(); // 釋放檔案載入
_findclose(handle); // 釋放遍歷目錄的控制代碼
}
return 0;
}