1. 程式人生 > >C++遍歷日誌log目錄,並提取資料進行分析

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;
}