2021年8月8日 星期日

Powershell 利用正則表達式 去除雙引號

Powershell 利用正則表達式 去除雙引號

tags: 部落格文章

這幾天遇到一個問題是CSS檔案裏面存在著大量的錯誤與法,顏色的代碼前後被上雙引號了,網頁差不多是15年前的產物了吧,那時候IE還沒這麼嚴苛或是標準還沒訂這麼細是能過得。

Powershell中使用正則表達式

有幾種方式最常見的是 -match 這只能回傳 bool 數值並不是這次要的這次是用了 -replace

還有一個問題是,Powershell 系統內建是 5.1 版,這個版本可不能處理非系統 encoding 和無 BOM的UTF8 這部分怎麼處理可以看之前的文章。簡單說就是升級到 7.1 就完事了。

[PowerShell 如何輸出 不帶BOM的 UTF-8 檔案](https://charlottehong.blogspot.com/2021/04/powershell-bom-utf-8.html

-replace

替代這個真的很好用。廢話不多說直接上代碼演示

$Text = "Copyright (C) Microsoft Corporation. 著作權所有,並保留一切權利。"
$Text -replace "Microsoft", "CHG"

直接就把原本內文中的 Microsoft 替換成 CHG 了。就大概這麼個用法。

其中有一個比較特別的用法是 $1 這個會回傳你的正則條件搜到的東西,就比如說這次的目標是要找到顏色代碼,但是我事先並不知道代碼是多少,我只能寫條件找到代碼,然後代碼要留著我要替除的是前後的引號。比如以下的示範

$ct = '#head_top{color: "#FFFFFF"};'
$dstStr = $ct -replace '"#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"', '#$1';
$dstStr

如此一來就可以把雙引號給去除了,剩下的工作就是把檔案讀取進來還有寫入。檔案讀取的時候如果需要過濾清單可以參考底下另一個用法。




-match

另外補充一下這個語法,最常見的是用來判斷文本裡面是否有目標字串,結果只會返回 True 跟 False 比如底下這樣的寫法。

# 文本
$Text = "Copyright (C) Microsoft Corporation. 著作權所有,並保留一切權利。"
# 搜尋是否有 Microsoft
$Text -match "Microsoft"

後面的地方是可以寫正則表達式的,有需要再自己嘗試一下。
然後正則搜到的什麼東西可以用 $matches[0] 來檢視

'#FFFFFF' -match '#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})'
$matches[0]

可以獲取顏色的代碼


獲取括號內的內容可以用這樣 (注意裡面的idx是用1)

'#head_top{color: (Abc123$#@%)};' -match '\((.*?)\)'
$matches[1]





進階應用

除了可以用來搜之外,其實還有一個特別的用法,可以用來剔除陣列中的東西

比如說我要排除特定檔名 txt 剩下的都保留,下面是檔案清單。先自己照著創各種不同類型的副檔名,直接創txt然後改副檔名就好。

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          2021/8/8  下午 01:33              0 main.cpp
-a----          2021/8/8  下午 01:33              0 note.txt
-a----          2021/8/8  下午 01:33              0 README.md

實際上的代碼

$FileItem = Get-ChildItem
$FileItem = $FileItem -notmatch ".txt$"
$FileItem

這樣就自動剔除 txt 結尾的副檔名了。
可以注意到我是用反過的帶有 no 的語法,如果反過來想指定白名單的話是這樣

$FileItem = Get-ChildItem
$FileItem = $FileItem -match ".cpp$|.md$"
$FileItem

可能有人會想到,不對呀 Get-ChildItem 本身就有過濾的 -filter 指令幹嘛還自己寫。雖然結果是一樣的,但其實兩者效能上有一點點區別。

還有最大的一點是能過濾資料夾!本身自帶的過濾器只能過濾附檔名是不能過濾資料夾的,比如說我讀取一個多層資料夾,裡面有一個 “.git” 文件我不想讀,這時候 -nomatch 就很好用了。這東西是用正則去判斷路徑的字串,自由度就非常高了。

2021年8月6日 星期五

git fork diff 比較 的時候中文亂碼

git fork diff 比較 的時候中文亂碼

其實這是git的問題,用任何圖形化工具也好都是吃git的API輸出比較的,包含vscode也是。在這些工具上比較的時候出現亂碼都可以設定。

fork這個工具本身是自帶git的,所以如果你要設定fork的亂碼問題要到fork的文件底下找到他自帶的git從裡面設定才有用。

如果是vscode上比較的亂碼直接修改安裝在系統上的git即可,或是你自己指定了位置就到那個位置去修改。

修改 Encodin 編碼

其實就只是 Big5 轉 utf8 所產生的問題而已,修改的方式到這個這資料去

# fork的git
%USERPROFILE%\AppData\Local\Fork\gitInstance\2.30.2\etc 

# 系統的git
C:\Program Files\Git\etc

底下有兩個文件是我們要修改的檔案,兩個都得改

gitconfig

# 日文
[diff "cp932"]
  textconv=iconv -f cp932 -t utf-8

# 中文

[diff "cp950"]
  textconv=iconv -f cp950 -t utf-8

gitattributes

# 日文
*.java  diff=cp932
*.sql   diff=cp932
*.xml   diff=cp932
*.html  diff=cp932
*.xsl   diff=cp932
*.css   diff=cp932
*.cpp   diff=cp932
*.hpp   diff=cp932
*.c     diff=cp932
*.h     diff=cp932

# 中文
*.java  diff=cp950
*.sql   diff=cp950
*.xml   diff=cp950
*.html  diff=cp950
*.xsl   diff=cp950
*.css   diff=cp950
*.cpp   diff=cp950
*.hpp   diff=cp950
*.c     diff=cp950
*.h     diff=cp950

需要什麼文檔在自己擴充,填完重新整理就好了

2021年8月3日 星期二

VScode + git 攜帶版如何把設置擋放在文件夾內而不是使用者文件

VScode + git 攜帶版如何把設置擋放在文件夾內而不是使用者文件

2022-10-07
事隔一年多找到最優解了,如果環境變數能從軟體設置當然是設置就完事了,關鍵是VsCode裡面有不少好用的外掛程序,外掛要讀或是甚至自己在終端機要用也會用不了。

關鍵的解法 Git攜帶版 要比要舒服的使用還是直接新增環境變數吧,兩個方式一個是啟動前在終端機加上臨時變數,然後再給終端機委託啟動。

$env:Path = $env:Path + "C:\git\bin"

另一個是直接加到使用者環境變數而不是系統。為什麼會卡到這個問題最大的原因也是因為公司的電腦基本沒管理員權限的。加到使用者就沒問題了

$UserPath = System.Environment]::GetEnvironmentVariable("PATH", "User")
System.Environment]::SetEnvironmentVariable("PATH", "$UserPath;C:\git\bin", "User")


如果電腦C曹使用者文件被封鎖沒有存取權限,又想要設置User層級的 gitconfig 的話可以更改git的家目錄,設定檔在 git\etc\profile 這檔案裡任意位置加上一行 Home="指定的位置" 就可以改了。




以下是從VsCode上直接設定git位置的舊文,因為有些副作用在建議用上面的做法。










這問題爬好我好一段時間,不知道為什麼沒什麼人提到,雖然說明是有的只是關鍵不知道怎麼下繞了好長的遠路,大概整理一下怎麼弄

VScode

首先先到官方下載,官方本身就有提供攜帶版的,只不過資料還是存在使用文件就是了。要怎麼才能把文件存在攜帶版的資料夾內。

其實就只是在 code.exe 的旁邊建立一個 data 的資料夾就好了,程式會自動會把設定跟擴展檔案丟在裡面。

Git for windows

這個問題更棘手一點,也是沒關鍵字很難找,雖然這個官方本身也是提供攜帶版,但是不知道為什麼設置擋還是存在使用者文件,我所在的環境就是沒權限存在使用者文件,打開直接就報錯…

查了很多文章基本上主流都是用一個 bat 檔來強制導入環境變數,雖然一開始我也是這個思路,但是所在環境嚴苛直接不給跑bat

繞了好大一圈發現其實直接在VScode內設置git的位置就好,然後還要到git內設置全域的gitconfig,設置之後就不會去讀使用者文件的gitconfig了

設置的方式直接點左下角齒輪,打開設置然後搜尋 git.path 自動引導到自制,選擇到 setting.json 內設置,他會幫你補上開頭,直接把git路徑丟進去就好

進去的時候路徑的斜線要自己打成兩個斜線,比如說

"git.path": "C:\\Program Files\\Git\\bin\\git.exe"

這樣就可以過了,剩下的問題就是終端機有時候需要手動輸入命令還是得建立一下。

新增終端機的語法有更新過,網路上大半都舊的不是很好用。大概記錄一新的樣式代碼。

"terminal.integrated.profiles.windows": {
        "UserDefGit": {
            "path": "C:\\Program Files\\Git\\bin\\bash.exe",
            "icon": "terminal-bash"
        }
    }

就長這樣啦,直接看就能理解了。想換圖標可以把後面整串砍掉自己手打 termi 就會跳出有哪些圖標可以用了。

如果要設置某個終端機為預設終端機,每次打開會優先打開的終端可以多打一行

"terminal.integrated.defaultProfile.windows": "UserDefGit"

後面的名稱要配合你自己寫的標籤名稱。你也可以直接從右下角的新增終端的時候,最下面有一個選擇預設值,直接按也可以按完設定會自己跑出上面那行。

最後如何輸入gitconfig位置在

git\etc

這裡是全域設置檔,我不知道怎麼搞出使用者設置擋,但至少全域設置是有效的,這招我從fork上面看來的,在fork上輸入的使用資訊也是存在自帶的git全域設置上。

2021年8月1日 星期日

git 還原刪除的分支 branch

如何 還原 錯誤被 刪除 的 branch

說來悲劇我在用Fork刪除本地分支的時候不小心勾選到刪除伺服器上的,導致服務器上的分支也被刪除了。沒弄回來我們這群人這幾個月的活都白幹了…好在是有找回來,下面記錄一下流程。



搜尋被刪除的分支

分支被刪除之後就丟失信息了,唯一能查的只有當前還沒有被git清理掉的野指標(reachable commit)有哪些


查詢野指標 (連按三下選取一行)

git fsck --full --no-reflogs --unreachable --lost-found | grep commit


這樣就能查到了,更進階一點可以把commit信息查出來(連按三下選取一行)

git fsck --full --no-reflogs --unreachable --lost-found | grep commit | cut -d\  -f3 | xargs -n 1 git log -n 1 --pretty=oneline

如此一來就可以更方便查找正確的提交點了

如果想要查看更詳細的提交的時間、上色等,可以這樣用。

git fsck --full --no-reflogs --unreachable --lost-found | grep commit | cut -d\  -f3 | xargs -n 1 git log -n 1 \
    --date=format:'%Y-%m-%d %H:%M:%S' \
    --pretty=format:"%C(reset)[%cd] %C(yellow)%h %C(cyan)<%cn> %C(reset)%s" 

更詳細的提交信息如何查看,可以看官方的說明:https://git-scm.com/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2


只不過這個搜尋到的是當前野指標,之前刪除過的分支也會存在沒辦法區分開。預設的刪除自動回收垃圾的時間是兩周,兩周內產生的野指標保證都會在。

只能自己看提交信息過濾,至少能保證被你幹掉的分支一定能找回來。只是可能會有多餘的提交點,多的東西時間久了一定會知道,沒時間過濾就先放著吧。


2023-10-19

追加一個顯示前一個節點的,雖然有試圖只顯示所有分之的最後結點,不過寫起來太長了,拿到這個資料之後,自己再丟程式處理

git fsck --full --no-reflogs --unreachable --lost-found | grep commit | cut -d\  -f3 | xargs -n 1 git log -n 1 \
    --date=format:'%Y-%m-%d %H:%M:%S' \
    --pretty=format:"%C(reset)[%cd] %C(yellow)%h%C(green) <-%p %C(cyan)<%cn> %C(reset)%s" 




重建分支

分支名稱可以當作是提交點的名別,這個資訊刪掉就沒了,沒辦法得知這個提交點過去是哪個分支,通常提交都會有規範要一併打上分支的名字,可以順著這個名字重建回來。如果沒有那就只能靠自己回想了,或是找到要復原的提交點在自己想個新的建立吧。

還原方式是先在找到所需要的提交點建立新的分支(取新的別名)即可。建立分支有兩種方式,任選一個就好。

# 建立新分支,並切換到新分支
git checkout "提交點的哈希碼" -b "要重建的新分支名"

# 建立新分支,停留在原本分支不切換
git branch "要重建的新分支名" "提交點的哈希碼"

這樣就能還原被刪除的分支了,一個一個慢慢還原就好



參考

git branch分支 和 tag標籤 的差別

git branch分支 和 tag標籤 的差別

一句話描述的話

他們都是用來替提交點取別名,省得我們輸入一長串看不懂的亂碼。
標籤只能標記一個哈希碼,分支則是包含起點與終點,並且終點會自動更新。




提交點

git的提交點,每一次的提交都會產生一個HashCode哈希碼,這個碼是一串亂碼用來當作提交點的唯一識別碼。

提交點是具有向前追蹤功能的,任一提交點都會記錄上一個提交點,有點類似單向鏈結這樣可以一直往前追蹤連到最初始的點。

這邊有一個小小的知識點不管你做什麼操作都不會被刪除。只要記住提交點的哈攜碼就隨時都能返回。

git上的任何指令操作都不會破壞提交點,包含撤銷這次提交也是不會刪除。

提交點是一串亂碼很難被記住,所以有兩個方式可以為他們取暱稱,也就是分支和標籤。同一個提交點並不限一個標籤、分支。

標籤

標籤只是單獨為提交點取另一個名字方便呼叫而已,本身基本等同於哈希碼的重新命名。要說區別就是被標籤住的提交點,不會因為放太久而被清除。

利用剛剛說的特性,git提交點絕對不會被指令刪除,還有每個提交點都有上一點的紀錄。這樣把提交點取個tag就永遠不會遺失了。

如果要做什麼危險操作的時候可以直接把當前分支tag起來,這個tag保證可以讓你回到最初的狀態。

就算你不tag單純只是把提交點的哈希碼記住也是一樣意思,只是時間不能拖太久,一段時間後無效提交點會被git自動回收刪除掉。

無效提交點:打個比方跟野指標一個意思,該提交點不在任何標籤或分支的鏈結內。

分支

分支跟標籤相比起來多了兩個功能

  1. 自動追蹤新的提交點
  2. 會記錄起始點

自動追蹤新的提交點

創建倉庫之後的預設名字 master 就是一個分支,分支會指向一個提交點,當這個提交點執行git命令產生新的提交點的時候,分支會自動更新指向的提交點。

比如說 現在在A點,當我執行git命令產生新的提交點B的時候,分支會自動更新指向的點,指向新的B點。

當我執行撤回提交的命令時也不是刪除C點而是單純,把分支指向的位置從C改成B而已。

包含起始點

在絕大多數的情況下,所謂的起始點就是從主線出來的那個點,基本上不會理會起始點的問題。

只是有些極端情況下會問題,就是你的分支是在提交之後建立的,因為從分支圖上面完全看不出來可能會被坑。

模擬一個情況,你提交的時候忘記切分支直接提交了,這時候提交點是在主線上,好死不死這段期間主線又更新了,你就很尷尬變成這個狀態,你的提交點是個野指標

a.jpg

然後你又直接對這個點新增分支

b.

看上去很完美對吧,然後因為看上去沒問題開心的繼續改代碼提交

c.

圖很完美完全正確,事實上剛剛說野指標的地方到時候rebase -i 的話會發現壓根就不會出現

2021年7月5日 星期一

Windows11 升級檢測 安全開機不相容【MBR無損轉GPT】

Windows11 檢測不相容怎麼解決【開機方式、硬碟分割類型、安全開機】



最主要的問題是出在你的硬碟是用MBR安裝的,早在2016年左右就已經全面支援EFI啟動了,如果不是太舊,主機板肯定是支援的。

如果你沒有把硬碟的格式分割的太誇張超過3個分區以上,是可以直接無腦轉換的。

快速轉換 mbr2gpt

這個是微軟內建的轉換工具,可以安心使用,沒bug也沒病毒。

這個有條件限制,必須是三個主分區以內才能轉,其他任何狀況都會出問題。
操作前要注意主機板有沒有支持EFI啟動。(英特爾第二代開始H61以後都有)

使用方式很簡單,直接在需要轉換的電腦操作下面步驟:

  1. 開啟 RE 中的 命令提示符
    (按住SIFT->再按重新開機->疑難排解->命令提示字元)


  2. 重啟之後會需要選擇帳號,並輸入密碼再來會開啟終端機

  3. 輸入 mbr2gpt /validate 檢查是否可轉
  4. 輸入 mbr2gpt /convert 轉換完畢
  5. 重新啟動

要是萬一進不去,可能你把修復分區給砍了,一樣是要打指令恢復,參考這篇文章
https://charlottehong.blogspot.com/2018/02/windows-re.html

這個在不行就只能從Win10的安裝光碟或USB啟動PE來打指令了,詳細參考這篇舊文
https://charlottehong.blogspot.com/2017/11/windows-mbr-gptefi.html

最後重啟之後要是不能開機

一般來說UEFI(舊稱BIOS)預設就是支援GPT分區開機的,所以改完之後理論上是可以直接啟動沒問題的。

如果真的不行,估計是開機選單的順序有問題直接還原一下UEFI到預設就好了。恢復之後預設會自動讓 Windows Boot Manager 當第一個就能開機了。

2021年6月10日 星期四

如何使用 forrange 疊代 class 的成員

如何使用 forrange 疊代 class 的成員

tags: 部落格文章


什麼是 forrange

這東西其實很好用,也很容易在別的代碼裡看到,他長這個樣子

vector<int> v{1, 2, 3};
for(auto&& i : v)
    cout << i << endl;

上面這個例子就會自己把結果疊代出來了,forrange這個東西展開來其實是用iterator實現的,差不多會等於下面的代碼

for (Vector<int>::iterator it = v.begin() ; it != v.end(); ++it)
    std::cout << *it << std::endl;

由這邊可以看出來我們需要實現的有這幾個大項

  1. Range::begin();
  2. Range::end();
  3. class iterator{};
  4. iterator::operator==()
  5. iterator::operator!=()
  6. iterator::operator*()



建立一個class

首先先讓我們把自己的class建造出來,大概就是簡單管理一個陣列即可。

class Range {
public:
    using _type = int;
public:
    Range(){}
    Range(initializer_list<_type> l): len(l.size()) {
        arr = new _type[l.size()];
        std::copy(l.begin(), l.end(), arr);
    }
    ~Range() {
        delete[] arr;
    }
private:
    _type* arr = nullptr;
    size_t len = 0;
};


iterrator 類別

差不多就這樣簡單即可,接下來要建造一個 iterrator 的類別(直接包在類別內就可以),還有完成剛剛提到那幾個函式。

在這個類別裡需要兩個成員資料,一個是我們自己建造的類別,一個是當前的指針跑到第幾個了。

struct Iterator {
    private:
        Range const* r;
        int current;
}

接下來是個別的函式

    struct Iterator {
    public:
        Iterator(Range const* x, size_t c) : r(x), current(c) {}
        Iterator& operator++() {
            ++current;
            return *this;
        }
        Iterator operator++(int) {
            Iterator old = *this;
            operator++();
            return old;
        }
        const _type& operator*() const{
            return r->arr[current];
        }
        friend bool operator==(Iterator const& x, Iterator const& y) {
            return x.current == y.current;
        }
        friend bool operator!=(Iterator const& x, Iterator const& y) {
            return !(x == y);
        }
    private:
        Range const* r;
        int current;
    };

建造好之後就可以使用摟


完整範例

#include <iostream>
#include <Iterator>
#include <initializer_list>
using namespace std;

class Range {
public:
    using _type = int;
public:
    Range(){}
    Range(initializer_list<_type> l): len(l.size()) {
        arr = new _type[l.size()];
        std::copy(l.begin(), l.end(), arr);
    }
    ~Range() {
        delete[] arr;
    }
private:
    _type* arr = nullptr;
    size_t len = 0;

public:
    struct Iterator {
    public:
        Iterator(Range const* x, size_t c) : r(x), current(c) {}
        Iterator& operator++() {
            ++current;
            return *this;
        }
        Iterator operator++(int) {
            Iterator old = *this;
            operator++();
            return old;
        }
        const _type& operator*() const{
            return r->arr[current];
        }
        friend bool operator==(Iterator const& x, Iterator const& y) {
            return x.current == y.current;
        }
        friend bool operator!=(Iterator const& x, Iterator const& y) {
            return !(x == y);
        }
    private:
        Range const* r;
        int current;
    };
    Iterator begin() const {
        return Iterator(this, 0);
    }
    Iterator end() const {
        return Iterator(this, len);
    }
};

int main() {
    Range r{1, 2, 3};
    for (auto&& i : r) {
        cout << i << ", ";
    } cout << endl;
}

參考

  1. https://cpprefjp.github.io/lang/cpp11/range_based_for.html
  2. https://stackoverflow.com/questions/46431762/how-to-implement-standard-iterators-in-class
  3. https://stackoverflow.com/questions/13407309/c-iterator-within-a-class/13407381
  4. https://www.cplusplus.com/reference/iterator/iterator/