2021年4月17日 星期六

PowerShell 安裝主題讓 終端機更好讀

PowerShell 安裝主題讓 終端機更好讀

廢話不多說先上圖



推下一行比較好讀一行是這一次執行命令的,哪一個行開始是上一次命令的,常常看半天。有了這麼粗一條的主題之後就不會有問題了。

唯一讓我覺得是不好的地方是列印內建的項目的時候,項目和項目中間每一行都會多空行,導致看起來很零散要捲很久一直看下去。這個我覺得是蠻糟糕的設計,有要瀏覽檔案我會把主題關掉。



安裝主題

設置權限

在開始之前需要先設置Powershell的權限,設置之後才能執行安裝的指令。
輸入之後需要手動輸入 Y 並按下 Enter 才能繼續。

Set-ExecutionPolicy RemoteSigned -scope CurrentUser

安裝主題套件

主題套件有兩個,輸入之後需要手動輸入 Y 並按下 Enter 才能繼續。

Install-Module posh-git
Install-Module oh-my-posh

執行主題

到這邊就已經好了,再來只需要啟動主題就好。

Import-Module posh-git
Import-Module oh-my-posh
Set-PoshPrompt -Theme Paradox


如何自動開啟

剛剛的主題雖然是啟動成功了,但不過這只有對當前視窗有效,關閉重開一個就沒了。如何自動開啟需要自己配置啟動設置檔。

新增配置文件

輸入下面指令如果沒有建立過則自動建立新檔案,建立完畢之後會自動打開記事本。如果已經有配置檔存在,就會直接打開文件。(不小心關了再打一次就好)

if (!(Test-Path -Path $PROFILE )) { New-Item -Type File -Path $PROFILE -Force }notepad $PROFILE

然後在記事本裡面輸入下面的內容,並存檔就可以關閉了。

# 自動配置主題Set-PoshPrompt -Theme Paradox}
# 路徑預設在桌面
cd ~\Desktop\



字體調整 - 更紗黑體

再來要處理的就是醜醜的新細明體,這個真的醜到不行。

推薦安裝 更紗黑體 下面是載點。
點進去之後下載TTC的版本就可以了,下載下來解壓縮後直接雙擊安裝。

https://mega.nz/file/s8tkxBwI#__XbfNawXftWIdoNzJvC_DqZawPFhBJEruL25Dbw714

安裝好之後就開始吧。重新開啟一個 PowerShell 視窗

對著最上面的地方按右鍵內容



調整一下字體。記得要選”等距”,因為是ttc包也同時安裝了沒”等距”的字型


再來色彩的部分建議調整成灰底比較順眼些,灰底的RGB值3個都是30
底下的透明度要不要調整可以看個人(可以偷看背景的指令邊看邊打)


2021年4月14日 星期三

將多個git倉庫合併為一個新的git倉庫

將多個git倉庫合併為一個新的git倉庫

這個可能發生的概率不大,我會遇到是因為在做練習的時候是個別對每個專案追蹤,產生無數個git倉庫,最後整理的時候想一口氣上傳到github備份才想到說這麼多個怎麼處理。

當然最簡單的就是個別把git倉庫移除,也就是直接暴力刪除隱藏資料夾 .git 文件就可以從最外層重建一個倉庫一口氣追蹤全部檔案,不過這樣也會導致之前寫的commit信息不見就是了。

想要保留commit信息,找了很多方法最後是有搞定大概說明一下怎麼做。

以下指令是順著一直做下去的,不要中圖關掉了可能會導致錯誤。


建立模擬倉庫

先說明一下範例檔案,現在作為示範先個別建立幾個子倉庫

樹狀結構長這個樣子

到時候做好的主倉庫也會像這個樣子的結構

git
├── git1
│   └── project1.md
├── git2
│   └── project2.md
└── git3
    └── project3.md

個別在資料夾內提交一個信息模擬要處理的情況

git init
git add *
git commit -m project1的提交信息
cd ..

三個都做或是只做一個可以,剩下的都一樣的指令,改位置而已


建立新的主倉庫

再來建立一個新的倉庫,這個用來當合併所有倉庫後的主倉庫的
我把它取名 git0 位置是在剛剛的樹狀圖 git 資料夾下跟其他git倉庫放一起

主倉庫裡面就放一個 README.md 這個自己建立。直接創一個 新文字文件.txt 也行隨便放點東西。

mkdir git0
cd git0
git init
git add *
git commit -m 初始化總倉庫

好了再來就準備下一步了,現在建好樹狀圖會像這樣

git
├── git0
│   └── README.md
├── git1
│   └── project1.md
├── git2
│   └── project2.md
└── git3
    └── project3.md


在主倉庫新增遠端庫

先新增鏈結並更新

git remote add git1 C:\Users\hunan\Desktop\git\git1
git fetch --all --tags

切到該節點並新增一個分支追蹤

git checkout git1/master
git checkout -b fix1

這裡的分支千萬記得要一開始就建立,分支會紀錄起點在哪裡。到時候合併後的 commmit 紀錄只會追到建立分支之前的提交點。

移動檔案打包到資料夾內

mkdir git1
git mv project1.md git1
git add *
git commit -m 倉庫1打包完成


合併倉庫

合併要分兩個部分,先在支線把檔案打包到新資料夾內,然後在把這個支線rebase到主線,最後在切回主線把這個分支併入。

從分支rebase主線

git rebase master

這裡我開一個新資料夾包進去,就不會有檔案變更的衝突
如果你不是這樣做發生衝突要自己在這裡解決才能下一步

回到主線把剛剛分支合併

git checkout master
git merge fix1 --no-ff

—no-ff 會把當初合併的分支的線路顯示出來,合併後就不是一條直線,而是可以看到過去是一條分支合併進來的。

詳細可以參可底下的圖,那條黃線就是指令弄出來的,沒加就只有一條橘線,個人覺得這樣比較清晰,加不加看情況和自身的習慣。


移除遠端庫和分支

最後就是處理殘留下來的垃圾了,上圖中的 fix1 標記,和最底下的 git1/master 這兩個標記是可以移除的。

git remote remove git1
git branch -d fix1

分支可以留方便記錄,遠端庫是本機的完全沒用建議砍了


結束

好啦至此就完美解決了,線路也很完美漂亮。全部搭完會有一條很完美的長線吧

缺點就是工作量有點大,如果專案太多手動打可能打不完。我現在就處於這個狀況,要不寫個批次檔給他跑,不然就算了…好麻煩,直接放棄紀錄全部重新提交不香嗎QuQ

2021年4月7日 星期三

C 讀寫檔案 範例代碼 [行讀寫.一次讀寫全部 ]

C 讀取檔案 範例代碼

如果是可以使用C++的環境,建議是用C++比較好寫也比較不用擔心指標的問題,C++的範例代碼可以參考這一篇。
https://charlottehong.blogspot.com/2021/04/c.html

寫了兩個版本,一個是單純函式讀取要自己管控字串的記憶體。另一個是類似於封裝成物件的方式自動管理,新手用第二個可能比較好,省去字串管理記憶體的問題,這問題通常是新手出bug的點。

不囉嗦直接上代碼

1. 讀檔的純C函式

使用方式直接輸入檔名就好,會自動讀出來,檔案記得自己創建好。

#include <stdio.h>
#include <stdlib.h>

void ReadFile_Example(const char* name) {
    FILE* pFile;
    long lSize;
    char* buffer;
    long result;

    // 開檔
    if (fopen_s(&pFile, name, "rb") || pFile == NULL) {
        fputs ("File error",stderr); exit (1);
    }
    // 獲取檔案長度
    fseek (pFile , 0 , SEEK_END);
    lSize = ftell (pFile);
    rewind (pFile);
    // 創建所需長度
    buffer = (char*)malloc(sizeof(char)*lSize+1);
    if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
    buffer[0] = 0;
    // 讀取檔案
    result = fread(buffer, sizeof(char), lSize, pFile);
    if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
    buffer[lSize] = 0;
    printf("%s", buffer);
    // 釋放資源
    fclose (pFile), pFile=NULL;
    free (buffer), buffer=NULL;
}

int main(int argc, char const* argv[]) {
    ReadFile_Example("a.txt");
    return 0;
}

好啦~拿了就用了,沒看過這麼貼心的教學文吧,貼上直接能執行不囉嗦。
下面開始正經解說了。




開檔案 fopen()

FILE* pFile;
// 方法1 (推薦)
fopen_s(&pFile, fileName, "rb");
// 方法2
pFile = fopen(fileName, "rb");

就這樣簡單一行就可以打開了,這邊我用比較新的標準 fopen_s() 而不是 fopen() 差別在於多一個 s 是更安全的意思,詳細在自己google查一下吧,總而言之是一樣的東西寫法有一點點不同而已。

打開之後需要檢查檔案有沒有開成功,失敗的原因有很多,最常見的是檔名打錯。失敗並不會像C++直接噴例外大可無腦不管。

檢測方式直接看他返回的指標是NULL還是有東西就可以了。他可以直接寫在if裡面節省行數,像底下這個樣子。

// 開檔
void openFile(FILE** p, const char* name, const char* mode) {
    if (fopen_s(p, name, mode)) {
        fputs ("Open file error",stderr); exit (1);
    }
}

這邊簡單處理直接噴一個 exit (1) 就不管了,如果開檔失敗還有相對應的例外處理就是從返回值那邊下手,返回布林值1或0表示成功與失敗。

直接 exit(1) 就是一旦失敗就直接結束程式了。返回值是失敗可以有對應方式,重開或是跳過之類的選項。

函式的使用方式像這個樣子

FILE* pFile;
openFile(&pFile, "a.txt", "rb");

獲取檔案長度

讀取的方式也是有依照特定長度讀取 fread( )與內建的行讀取 getline(),如果想用前者一口氣把全部讀完就需要知道檔案有多長了。

計算長度的方式是利用讀取指標計算的,他就像你打開記事本的時候出現的游標一樣意思,一格一格慢慢前進,讀到最後面遇到結束符號為止。

想知道檔案長度就把游標移動到最後面,然後算一下多少格就是了,最後再把檔案移動回原本的位置即可。

long getFileSize(FILE* p) {
    long temp = ftell (p), lSize = 0;
    fseek (p , 0 , SEEK_END);
    (lSize) = ftell (p);
    fseek (p , temp , SEEK_SET);
    return lSize;
}

fseek() 中的是表示偏移距離與相對位置。定位參數總共有3個。

// 從結束點開始計算偏移0格
fseek (p , 0 , SEEK_END);
// 從起點開始計算偏移 temp 格
fseek (p , temp , SEEK_SET); 
// 從當前位置開始計算偏移 -1 格
fseek (p , -1 , SEEK_CUR);

創建動態陣列

也可以用非動態的陣列存,只是檔案大小不可預測,管控動態陣列是不可少的。這邊直接提供一個函式給大家用。

檢測兩次NULL的意思是因為,第一檢測要知道進來的東西有沒有連接著別的動態陣列,要是不檢查直接把原本指的的東西給丟了,直接就 memory leak 了。第二次檢測是要知道系統有沒有真的配發下來,記憶體要是滿了系統是沒辦法給的。

最後再給個結束符,字串的操作千萬記得給一下,沒有給沒辦法正常讀。有所有的字串相關函式都是依靠結束符運作的。

// 創建動態陣列
void createString(char** str, int len) {
    if (*str != NULL) {fputs ("str is not NULL",stderr); exit (2);}
    *str = (char*)malloc(sizeof(char) * len);
    if (*str == NULL) {fputs ("Memory error",stderr); exit (2);}
    *str[0] = 0;
}

要新增就這樣用,這樣就會自動建立了。只是用完後要記得自己釋放。

char* str=NULL;

createString(&str, 10);
printf("%s\n", str);

free(str), str=NULL;

不是太大重點,如果不明白什麼意思,複製貼上就是了。

讀取檔案

讀取的參數 read()

// 讀取檔案
fread(buffer, sizeof(char), lSize, pFile);
buffer[lSize] = 0;
printf("%s", buffer);

buffer 是依據檔案長度建立的字串 char*
lSize 是檔案的總長度

其中間的這一行特別重要

buffer[lSize] = 0;

因為讀檔並不包含字串的結束字符,必須自己設置。估計很多人卡關會卡在這裡,這也引出一個問題是字串的創建長度必須是,檔案長度的+1才可以,還要裝最後一個結束符。

以上就完成拉,想練習可以把三個函式奏起來,組合成一個可以讀檔的程式,當作練習。

打開的參數

簡單整理一下懶人包

模式參數說明
r[讀檔], 有+號時 檔案必須存在不會自動建立
w[寫檔], 有+號時 檔案可以不存在會自動建立新的
b在 Windwos 下必須追加
+可讀可寫
a從尾部追加

其中 b 的意思直接把他當作windwos上必須寫上就好了。不然讀取的時候微軟的換行標記 \r\n 會被讀成只有 \n 導致長度錯了,進而導致可能越界讀到垃圾值。
可以參考這一篇:https://blog.csdn.net/cattylll/article/details/7107089

詳細說明看下面的表格,最常用的就

參數詳細說明

模式參數說明
r開啟檔案進行唯讀,若檔案不存在,則傳回NULL
w開啟檔案進行唯寫,若檔案不存在,則建立新檔,若檔案存在則將之刪除,再建立新檔
a開啟檔案進行附加,若檔案存在,則資料從檔案尾端寫入,若檔案不存在則建立新檔
rb以二進位模式開啟檔案進行唯讀
wb以二進位模式開啟檔案進行唯寫
ab以二進位模式開啟檔案進行附加
r+開啟檔案進行讀寫,若檔案不存在,則建立新檔,若檔案存在,資料將從檔案開頭進行覆寫
w+開啟檔案進行讀寫,若檔案不存在,則建立新檔,若檔案存在則覆寫原有的資料
a+開啟檔案進行附加、讀取,若檔案不存在則建立新檔,若檔案存在,則資料從檔案尾端寫入
r+b以二進位方式開啟檔案進行讀寫
w+b以二進位方式開啟檔案進行讀寫
a+b以二進位方式開啟檔案進行附加、讀取







2. 封裝好的物件

對於新手來說,讀檔最大的難關應該是在字串的操作吧,必須自己手動管理。C是沒有物件的,指標的操作只能自己小心點知道在幹嘛。

我就就大概遵從物件的規則仿製了一個,類物件的操作方式,想練習可以看看我怎麼寫的,照著這個方式去寫程式,寫檔案的函式我留著沒寫,當練習把它寫出來吧。

代碼放最後面,先簡單說明一下怎麼用。

使用方式

宣告變數用這樣的方式

FileData* file = newFileData();

讀取檔案內建會自動管理記憶體直接取就好

FileData_getFileString(file, "a.txt");
printf("%s\n\n", file->data);

最後結束的時候記得釋放資源,在釋放之前是可以連續讀取不同檔案的。

FileData_dtor(file);

代碼

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct FileData{
    FILE* pFile;
    char* data;
    long size;
    long capa;
    bool init;
} FileData;
// 檢查是否由建構子初始化
void FileData_check(FileData* f) {
    if (f->init != true)     {
        fputs ("FileData not init.",stderr); exit (1);
    }
}
// 初始化
FileData* newFileData() {
    FileData* p = (FileData*)malloc(sizeof(FileData));
    if (p == NULL) {
        fputs ("Memory error",stderr); exit (2);
    } else     {
        p->pFile = NULL;
        p->data = NULL;
        p->size = 0;
        p->capa = 0;
        p->init = true;
    }
    return p;
}
// 解構
void FileData_dtor(FileData* f){
    FileData_check(f);
    if (f->pFile != NULL)     {
        fclose (f->pFile);
        f->pFile = NULL;
    }
    if (f->data != NULL)     {
        free (f->data);
        f->data = NULL;
        f->size = 0;
        f->capa = 0;
    }
}
// 讀取檔案到字串
void FileData_getFileString(FileData* self, const char* name) {
    // 檢查
    FileData_check(self);
    if (self->pFile != NULL) {
        fclose (self->pFile);
        self->pFile = NULL;
    }
    // 宣告
    FILE** pFile = &(self->pFile);
    char** buffer = NULL;
    // 開檔
    if (fopen_s(pFile, name, "rb") || !(*pFile)) 
        fputs ("Open file error",stderr),  exit (1);
    // 獲取檔案大小
    fseek (*pFile , 0 , SEEK_END);
    self->size = ftell (*pFile);
    fseek (*pFile , 0 , SEEK_SET);
    // 如果容量不足,重建所需長度
    buffer = &(self->data);
    if (self->capa <= self->size) {
        char* pStr= NULL;
        free(*buffer);
        pStr = (char*)malloc(sizeof(char) * self->size+1);
        self->capa = self->size+1;
        if (pStr == NULL) {fputs ("Memory error",stderr); exit (2);}
        *buffer = pStr;
    }
    fread(*buffer, sizeof(char), self->size, *pFile);
    (*buffer)[self->size] = 0;
    //printf("%s\n", *buffer);
    // 釋放資源
    fclose (*pFile);
}
// 讀取一行到字串
bool FileData_getFileLine(FileData* self, const char* name, long len) {
    // 檢查
    FileData_check(self);
    // 宣告
    FILE** pFile = &(self->pFile);
    char** buffer = NULL;
    // 開檔
    if(self->pFile == NULL){
        if (fopen_s(pFile, name, "rb") || !(*pFile)) 
            fputs ("Open file error",stderr),  exit (1);
    }
    // 獲取檔案大小
    self->size = len;
    // 如果容量不足,重建所需長度
    buffer = &(self->data);
    if (self->capa <= self->size) {
        char* pStr= NULL;
        free(*buffer);
        pStr = (char*)malloc(sizeof(char) * self->size+1);
        self->capa = self->size+1;
        if (pStr == NULL) {fputs ("Memory error",stderr); exit (2);}
        *buffer = pStr;
    }
    if (fgets(*buffer, self->size, *pFile) != NULL) {
        return true;
    }
    // 沒有輸出清空
    *buffer[0] = 0;
    return false;
}
// 覆蓋寫檔案
void FileData_overWrite(FileData* self, const char* name, const char* inStr) {

}

// 測試
void test_getFileString() {
    FileData* file = newFileData();
    FileData_getFileString(file, "b.txt");
    printf("%s\n\n", file->data);
    FileData_getFileString(file, "a.txt");
    printf("%s\n\n", file->data);
    FileData_getFileString(file, "c.txt");
    printf("%s\n\n", file->data);
    FileData_dtor(file);
}
void test_getFileLine() {
    FileData* file = newFileData();

    while(FileData_getFileLine(file, "a.txt", 100)) {
        printf("%s", file->data);
    } printf("\n");

    fseek (file->pFile , 0 , SEEK_SET);
    while(FileData_getFileLine(file, "a.txt", 101)) {
        printf("%s", file->data);
    } printf("\n");

    FileData_dtor(file);
}
//==============================================================
int main(int argc, char const* argv[]) {
    test_getFileString();
    test_getFileLine();
    return 0;
}
//==============================================================