在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 0x0D-單線程備份(上)
0x0E-單線程備份(下)
0x11-套接字編程-1
0x05-C語言指針:(Volume-1)
0x13-套接字編程-HTTP服務(wù)器(1)
0x0C-開始行動
C 語言進階
第一部分
0x05-C語言指針(Volume-2)
0x08-C語言效率(下)
0x07-C語言效率(上)
0x04 C代碼規(guī)范
0x0F-多線程備份
0x05-C語言變量
第四部分
0x16-套接字編程-HTTP服務(wù)器(4)
0x0D-單線程備份(上)
總結(jié)
0x01-C語言序言
0x15-套接字編程-HTTP服務(wù)器(3)
0x14-套接字編程-HTTP服務(wù)器(2)
0x17-套接字編程-HTTP服務(wù)器(5)
第三部分
我的C語言
0x06-C語言預(yù)處理器
0x09-未曾領(lǐng)略的新風(fēng)景
0x0A-C線程和Glib的視角
第二部分
0x10-網(wǎng)絡(luò)的世界
0x12-套接字編程-2
0x03-C代碼
0x0B-C語言錯誤處理

0x0D-單線程備份(上)

0x0D-單線程備份(上)

寫在最前方

  • 源路徑:即 From-Path,你準備要備份的資料
  • 目的路徑: 即 To-Path,你準備要存儲備份的資料的地方
  • 稍微回想一下,上一次寫的代碼,本次的任務(wù)是遍歷目錄及其子目錄,那么這回要干的就是將上次遍歷過的數(shù)據(jù),挪一下窩,到我們想要他們?nèi)サ奈恢谩?/li>
  • 這涉及到兩個操作,遍歷拷貝,前一個動作我們在上一回已經(jīng)實現(xiàn)了,只需做小小的改動,就能夠使用。后一個動作也是需要靠 Windows API來完成,至于哪些,稍后再提。
  • 現(xiàn)在先讓我們完成一個魔法,3, 2, 1!

      do{
          puts("-------------------------------------------------");
          fprintf(stdout, "The Default Path is : %s \n", DEFAULT_TO_PATH);
          fprintf(stdout, "Now The Path is     : %s \n", get_backup_topath());
          puts("-------------------------------------------------");
          puts("That is a System Back Up Software for Windows! ");
          puts("List of the software function : ");
          puts("1. Back Up ");
          puts("2. Set Back Up TO-PATH ");
          puts("3. Show TO-PATH History");
          puts("4. Read Me ");
          puts("5. Exit ");
          puts("-------------------------------------------------");

    對界面稍微有了一些改動。

    新增了第三行和第四行的 系統(tǒng)默認目的路徑和當(dāng)前使用的目的路徑。

    新增了倒數(shù)第四行的查看目的路徑歷史紀錄的功能。

    main函數(shù)外頭需要 extern DEFAULT_TO_PATH;因為引用了setPath.c里的一個全局變量。

寫在中間

  • 前一次我們曾經(jīng)提到要讓函數(shù)的功能更加清晰,為了達到這個目的,應(yīng)該把可能用到的一些原生庫函數(shù)包裹一下,讓可能發(fā)生的錯誤盡量掌握在我們自己的手里

  • 安全函數(shù)

    • 新建 safeFunc.h safeFunc.c
    • 考慮一下我們需要包裹的函數(shù): malloc, freefopen 三個庫函數(shù)。

      • 為了不讓后方的多線程實現(xiàn)產(chǎn)生更多的以后,不單獨使用全局錯誤輸出。
      • 讓我來將他們實現(xiàn)一下
      • 我不會省略一些看似不必要的東西,例如注釋,而是完整的呈現(xiàn)出來,如果覺得篇幅過長,可以選擇跳躍的閱讀。
      • 魔法來了,3, 2, 1!

              #include <stdio.h> /* size_t */
              #include <stdlib.h>
              #include <setjmp.h>
              #define TRY_TIMES 3
        
              typedef struct _input_para{
                  char * file; /* 待打開或創(chuàng)建的文件名 */
                  char * mode; /* 打開的模式 */
              }params;
        
              jmp_buf malc_jmp; /*Malloc_s*/
              jmp_buf fopn_jmp; /*Fopen*/
        
              /**
               * @version 1.0 2015/10/01
               * @author  wushengixin
               * @param   ... 參看結(jié)構(gòu)體說明
                          可傳入任意的個數(shù)的,形式為 .file = "xxx", .mode = "x" 的參數(shù)
               * function 用于使用默認參數(shù),并調(diào)用函數(shù) Fopen 進行打開操作
               */
              #define Fopen_s(...) Fopen((params){.file = NULL, .mode = "r", __VA_ARGS__})
              FILE* Fopen(const params file_open);
        
              /**
               * @version 1.0 2015/10/01
               * @author  wushengxin
               * param    sizes 輸入需要分配的大小
               * function 用于隱藏一些對錯誤的處理,并調(diào)用malloc庫函數(shù)分配空間
               */
              void * Malloc_s(size_t sizes);
        
              /**
               * @version 1.0 2015/10/01
               * @author  wushengxin
               * @param   input 外部傳入的等待釋放的指針
               * function 用于隱藏一些對錯誤的處理,并調(diào)用free庫函數(shù)進行釋放指針
               */
              void Free_s(void * input);

        里面用到了一些新的特性,如果使用 GCC/Clang作為編譯器的,記得要開啟-std=c11 支持。

          這幾個函數(shù)就不再詳細解釋,而是簡略說幾個,接下來放上實現(xiàn)代碼:
        
              FILE* Fopen(const params file_open)
              {
                  int times = 0;
                  FILE* ret_p = NULL;
                  if (file_open.file == NULL)
                  {
                      fputs("The File Name is EMPTY! Comfirm it and Try Again", stderr);
                      return ret_p;
                  }
                  setjmp(fopn_jmp); /* fopn_jmp To there */
                  ret_p = fopen(file_open.file, file_open.mode);
                  if (ret_p == NULL)
                  {
                      if (times++ < TRY_TIMES)  
                      longjmp(fopn_jmp, 0); /* fopn_jmp From here */
                      fprintf(stderr, "The File : %s Open with Mode (%s) Fail!\n", file_open.file, file_open.mode);
                  }
                  return ret_p;
              }
        
              void * Malloc_s(size_t sizes)
              {
                  int times = 0;
                  void * ret_p = NULL;
                  if (sizes == 0)
                      return NULL;
                  setjmp(malc_jmp); /* malc_jmp To There */
                  ret_p = malloc(sizes);
                  if (ret_p == NULL)
                  {
                      if (times++ < TRY_TIMES) /* malc_jmp From Here */
                          longjmp(malc_jmp, 0);
                      fputs("Allocate Memory Fail!", stderr);
                  }
                  return ret_p;
              }
        
              void Free_s(void * input)
              {
                  if (input == NULL)
                  {
              #if !defined(NOT_DEBUG_AT_ALL)
                      fputs("Sent A NULL pointer to the Free_s Function!", stderr);
              #endif
                      return;
                  }
                  free(input);
                  input = NULL;
              }
          第一個函數(shù)是用外部定義的宏 `Fopen_s`啟動它,這里沒有實現(xiàn)隱藏它。
        
          最后一個函數(shù)中使用了預(yù)處理的機制,如果在頭文件中定義了 `#define NOT_DEBUG_AT_ALL`,這個輸出將不在出現(xiàn)
  • 安全函數(shù)已經(jīng)撰寫完成,接下來就是干正事了

    • setPath.h

      • 我們首先要將程序里保存上默認的目的路徑,首先想到用常量#define ...
      • 其次應(yīng)該要確保當(dāng)前目的路徑不被其他非法的渠道訪問,那就應(yīng)該用一個static 字符數(shù)組存儲。
      • 接下來就是要提供一個函數(shù)當(dāng)作接口(這里用了接口這個術(shù)語不知道合不合適),來獲取當(dāng)前實際在使用的目的路徑 get_backup_topath
      • 這里還需要將之前實現(xiàn)過的 repl_str ,再次實現(xiàn)一次,因為之前的顯示功能只是測試,并不會實際應(yīng)用到程序當(dāng)中。
      • 完成這兩個功能函數(shù)以后,再去考慮實現(xiàn)怎么樣設(shè)置路徑,存儲路徑,以及使用文件流操作來緩存歷史目的路徑

              #include "safeFunc.h"
        
              #define SELF_LOAD_DEFAULT_PATH "C:/"
              #define MIN_PATH_NAME _MAX_PATH /* 最小的限制 */
              #define LARGEST_PATH_NAME 32767 /* 路徑的最大限制 */
        
              /*
               * @version  1.0 2015/10/02
               * @author   wushengxin
               * @function 用于返回當(dāng)前使用的目的路徑
               */
              const char * get_backup_topath();
        
              /**
              * @version 1.0 2015/09/28
              * @author  wushengxin
              * @param   src 外部傳入的,用于調(diào)整
              * @function 用于替換路徑中的 / 為 \ 的
              */
              void repl_str(char * src);
          對應(yīng)的實現(xiàn)中,會定義一個靜態(tài)的字符數(shù)組,且在頭文件中能夠看見,很多是在`showFiles`里定義過的。
        
          定義過的函數(shù),例如 `repl_str`需要把`showFiles.c`中的**實現(xiàn)**,使用`#if 0 ... #endif` 進行注釋掉,不然會發(fā)生重定義的錯誤。
      • setPath.c

              #include "setPath.h"
        
              static char to_path_buf[LARGEST_PATH_NAME] = SELF_LOAD_DEFAULT_PATH;
              const char * DEFAULT_TO_PATH = SELF_LOAD_DEFAULT_PATH;
              const int LARGEST_PATH = LARGEST_PATH_NAME;
        
              const char * get_backup_topath()
              {
                  return to_path_buf;
              }
        
              void repl_str(char * src)
              {
                  size_t length = strlen(src);
                  for (size_t i = 0; i <= length; ++i)
                  {
                      if (src[i] == '/')
                          src[i] = '\\';
                  }
                  return;
              }
      • 有了上面的代碼,主界面就再次能夠無誤運行了,那么剩下的就是實現(xiàn),設(shè)置目的路徑,存儲目的路徑到本地,顯示目的路徑,分別對應(yīng)主界面的2, 3。
      • 怎么實現(xiàn)比較好,再開始之前,分析一下會遇到的情況:
        • 我們在得到目的路徑之后,會將其拷貝給默認路徑 to_path_buf,并且將其存儲到本地緩存文件中,以便下次程序開始時可以直接使用上一次的路徑
        • 還可以使用另一個文件存儲所有用過的歷史路徑,包含時間信息。
      • 那么這就要求我們首先實現(xiàn)存儲目的路徑的功能,其次再實現(xiàn)設(shè)置目的路徑的功能,最后實現(xiàn)顯示目的路徑的功能
      • 注:兩個看似無用的全局變量(const)是為了其他文件的可見性而設(shè)立的,且相對于#define能夠省一些無足輕重的空間。

      • 存儲目的路徑 store_hist_path

        • setPath.h

                  #include <time.h>
                  /**
                   * @version  1.0 2015/10/02
                   * @version  wushengxin
                   * @param    path 需要存儲的路徑
                   * @function 用于存儲路徑到本地文件 "show_hist" 和 "use_hist" 
                   */
                  void store_hist_path(const char * path);
        • setPath.c

                  void store_hist_path(const char * path)
                  {
                      time_t ctimes; 
                      time(&ctimes); /* 獲取時間 */
                      FILE* input_use = Fopen_s(.file = "LastPath.conf", .mode = "w"); /* 每次寫入覆蓋 */
                      FILE* input_show = Fopen_s(.file = "PathHistory.txt", .mode = "a");
                      if (!input_show || !input_use)
                      {
                  #if !defined(NOT_DEBUG_AT_ALL)
                          fputs("Open/Create the File Fail!", stderr);
                  #endif
                          return;
                      }
                      fprintf(input_use, "%s\n", path); /* 寫入 */
                      fprintf(input_show, "%s %s", path, ctime(&ctimes));
                      fclose(input_show);
                      fclose(input_use);
                      return;
                  }
              `time`和`ctime` 函數(shù)的使用網(wǎng)路上的介紹更加全面,這里不做解釋。
          
              完成了存儲的函數(shù)之后,便是實現(xiàn)從鍵盤讀取并且設(shè)置默認路徑
      • 設(shè)置目的路徑 set_enter_path

        • 在此處需要停下來在此思考一下,如果用戶輸入了錯誤的路徑(無效路徑或者惡意路徑),也應(yīng)該被讀取嗎?所以應(yīng)該增加一個檢查,用于確認路徑的有效性。
        • setPath.h

                  #include <string.h>
                  #include <io.h> /* _access */
                  enum {NOT_EXIST = 0, EXIST = 1};
                  /**
                   * @version  1.0 2015/10/02
                   * @author   wushengxin
                   * @function 用于讀取從鍵盤輸入的路徑并將之設(shè)置為默認路徑,并存儲。
                   */
                  void set_enter_path();
          
                  /**
                   * @version  1.0 2015/10/02
                   * @author   wushengxin
                   * @param    path 用于檢查的路徑
                   * @function 用于檢查用戶輸入的路徑是否是有效的
                   */
                  int is_valid_path(const char * path);
        • setPath.c

                  int is_valid_path(const char * path)
                  {/* _access 后方有解釋 */
                      if (_access(path, 0) == 0) /* 是否存在 */
                          return EXIST;
                      else
                          return NOT_EXIST;
                  }
          
                  void set_enter_path()
                  {
                      int intJudge = 0; /* 用來判斷是否決定完成輸入 */
                      char tmpBuf[LARGEST_PATH_NAME]; /** 臨時緩沖區(qū) **/
                      while (1)
                      {
                          printf("Enter The Path You want!\n");
                          fgets(tmpBuf, LARGEST_PATH_NAME*sizeof(char), stdin); /* 獲取輸入的路徑 */
                          sscanf(tmpBuf, "%s", to_path_buf);
                          if (is_valid_path(to_path_buf) == NOT_EXIST)
                          {
                              fprintf(stderr, "Your Enter is Empty, So Load the Default Path\n");
                              fprintf(stderr, "%s \n", SELF_LOAD_DEFAULT_PATH);
                              strcpy(to_path_buf, SELF_LOAD_DEFAULT_PATH);
                          }
                          fprintf(stdout, "Your Enter is \" %s \" ?(1 for yes, 0 for no) \n", to_path_buf);
          
                          fgets(tmpBuf, LARGEST_PATH_NAME*sizeof(char), stdin);
                          sscanf(tmpBuf, "%d", &intJudge); /* 獲取判斷數(shù)的輸入 */
                          if (intJudge != 0)
                          {
                              if (to_path_buf[strlen(to_path_buf) - 1] != '/')
                                  strcat(to_path_buf, "/");/* 如果最后一個字符不是'/',則添加,這里沒考慮是否越界 */
                              store_hist_path(to_path_buf);
                              break;
                          } /* if(intJudge) */
                      }/* while (1) */
                      return;
                  }/* set_enter_path */
              這一組函數(shù)的功能稍微復(fù)雜,大體來說便是 `讀取路徑輸入->檢查路徑有效性->讀取判斷數(shù)->是否結(jié)束循環(huán)`
          
              其中`_access` 函數(shù)有些淵源,因為這個函數(shù)被大家所熟知的是這個形式 `access`,但由于這個形式是 **POSIX** 標準,故 **Windows** 將其實現(xiàn)為`_access`,用法上還是一樣的,就是名字不同而已。
      • 顯示歷史路徑 show_hist_path

        • setPath.h

                  /**
                   * @version  1.0 2015/10/02
                   * author    wushengxin
                   * function  用于在窗口顯示所有的歷史路徑
                   */
                  void show_hist_path();
        • setPath.c

                  void show_hist_path()
                  {
                      system("cls");
                      char outBufName[LARGEST_PATH_NAME] = {'\0'};
                      FILE* reading = Fopen_s(.file = "PathHistory.txt", .mode = "r");
                      if (!reading)
                      return;
          
                      for (int i = 1; i <= 10 && (!feof(reading)); ++i)   
                      {
                          fgets(outBufName, LARGEST_PATH_NAME*sizeof(char), reading);
                          fprintf(stdout, "%2d. %s", i, outBufName);
                      }
                      fclose(reading);
                      system("pause");
                      return;
                  }
      • 剩下最后一個收尾工作

        • 初始化路徑
        • 每次程序啟動的時候,我們都會讀取本地文件,獲取上一次程序使用的最后一個路徑,作為當(dāng)前使用的目的路徑
      • 初始化目的路徑 init_path

        • setPath.h

                  /**
                   * @versions  1.0 2015/10/02
                   * @author    wushengxin
                   * @function  用于每次程序啟動時初始化目的路徑
                   */
                  void init_path();
        • setPath.c

                  void init_path()
                  {
                      int len = 0;
                      char last_path[LARGEST_PATH_NAME] = { '\0' };
                      FILE* hist_file = Fopen_s(.file = "LastPath.conf", .mode = "r");
                      if (!hist_file) /* 打開失敗則不初始化 */
                          return;
                      fgets(last_path, LARGEST_PATH_NAME, hist_file);
                      len = strlen(last_path);
                      if (len > 1)
                      {
                          last_path[len - 1] = '\0'; /* 消除一個多余的 ‘\n’ */
                          strcpy(to_path_buf, last_path);
                      }
                      return;
                  }
              這樣就大功告成了,對于這個函數(shù)中的后`8`行代碼,沒使用慣用的`fgets 配合 sscanf` 是因為如果這么干的話,需要搭配一個`memset`函數(shù)清零,后面會有解釋。

寫在最后方

  • 具體思路代碼完全都貼出來了,除了主界面的某些細微區(qū)別沒有貼出來,但是自己應(yīng)該能夠完成。
  • 對于memset的解釋
    • 這個函數(shù)對于大的內(nèi)存塊的初始化實際上是很慢的,當(dāng)然我們這個30KB左右大概的內(nèi)存可能影響還沒有那么大,但是上以后,調(diào)用memset就是一種性能問題了,很多情況下,編譯器在開啟高優(yōu)化等級之后會自動幫你取消memset的隱式調(diào)用
    • 什么隱式調(diào)用,例如 init_path第二行代碼,聲明并且用花括號初始化這個數(shù)組的時候,就會調(diào)用隱式memset。

結(jié)束

  • 下一次要實現(xiàn)的就是,本程序的主體 備份