2021年5月12日 星期三

Win10 【修改日期】和【存取日期】有什麼差別不同處

Win10 【修改日期】和【存取日期】有什麼差別不同處

tags: 部落格文章

以前就一直很好奇這兩個東西到底有什麼不同,因為邏輯很奇怪,有比對過的人應該都會滿頭問號XDD

今天寫程式要改日期,不得不把他們弄懂只好乖乖爬文了,遺憾的目前能檢索到的中文資料居然沒一篇講清楚的,來補一下這個中文資源吧~



建立日期 CreationTime

這個應該很好懂就是這一份檔案什麼時產出來的,這個永遠都不會變動。

以下情況才會發生改變

  1. 複製的時候的時候,產生一份新檔案(修改日期會保留)
  2. 上傳到雲端在下載回來的檔案屬於新建檔案(通常修改會滯後於新建,時間差就是下載的時間)
  3. RAR解壓出來屬於複製檔案



修改日期 LastWriteTime

指的是對檔案的內容的變更,這個應該沒什麼疑念。
只要有修改內容就會更新時間了。



存取日期 LastAccessTime

這個才是最難理解的,看名字就覺得開啟檔案也算吧,很遺憾不一定會馬上反應。

這不是非常反邏輯不是就寫著存取了嗎?

事實上因為每次開檔就更改存取日期非常消耗系統效能,預設微軟把她關了,對就是給關了,但並不是完全關閉而是智能保護。

這個智能保護的邏輯可能隨著Windows系統更新被優化,我的當前版本是Win10 20H2

怎麼解除保護狀態呢,很簡單直接打開記事本給他修改一下後保存,就解除了。

當保護解除時,以下動作都會變動存取日期

  1. 查看檔案內容 (寫到緩存還沒更新到硬碟)
  2. 檔案內容修改
  3. 檔名修改
  4. 移動檔案
  5. 複製檔案

基本上就是名如其實,有存取都會紀錄

至於怎查看緩存的內容可以直接打下面這行指令

# 查看檔案存取日期
(Get-Item "Z:\a.txt").LastAccessTime


如何取消能保護可以執行這行指令
(沒需求的話別做死浪費硬碟效能)

# 關閉智能保護
# fsutil behavior set disablelastaccess 0

另外有一種情況開檔會變更”存取日期”,但不會變更”修改日期”,有些Excle或Word裡面是有帶腳本的的打開的瞬間腳本執行就會變更”存取日期”和”修改日期”,關閉檔案後因為沒變更軟體會自動把”修改日期”改回原本的日期。以至於打開又關閉一看,奇怪怎麼存取日期變了。



從日期判斷檔案有過什麼操作

實際做一些操作來試試看變化吧

更改檔名(未解除保護)

沒有變動

建立日期 2021/5/12 下午 05:32:57
修改日期 2021/5/12 下午 05:32:57
存取日期 2021/5/12 下午 05:32:57

移動檔案(未解除保護)

沒有變動

建立日期 2021/5/12 下午 05:27:29
修改日期 2021/5/12 下午 05:27:29
存取日期 2021/5/12 下午 05:27:29

複製檔案

建立與存取日期會更新

建立日期 2021/5/12 下午 05:26:32
修改日期 2021/5/15 下午 12:34:56
存取日期 2021/5/12 下午 05:26:32

修改內容

修改會滯後於修改(這段時間是你開檔到存檔的時間差)

建立日期 2021/5/12 下午 05:59:07
修改日期 2021/5/12 下午 05:59:36
存取日期 2021/5/12 下午 05:59:38




參考

2021年5月2日 星期日

chrome瀏覽器 開某些網站會 LAG 改用 EDGE 就好了怎麼處理

chrome瀏覽器 開某些網站會 LAG 改用 EDGE 就好了怎麼處理

這個問題我一直很好奇怎麼會這樣,我開Youtube、FaceBook都可以感受到。

尤其是舊版的FB卡的不要不要的,甚至是HackMD這種筆記網頁也會,後來在巴哈看到有人在討論的意外地解決了。

解決辦法

來這個頁面

chrome://flags/#use-angle

把Choose ANGLE graphics backend這個選項改成 D3D9 可以改善。
具體原理也不太清楚,貌似是和顯示卡有關吧~

個人是直接改用 Edge 棄用 chrome 了

2021年4月24日 星期六

PowerShell 5.1 如何輸出 不帶BOM的 UTF-8 檔案

PowerShell 5.1 如何輸出 不帶BOM的 UTF-8 檔案

說明是對於 PowerShell 5.1 版本,從6版本以上預設 Out-File 預設就是不帶 BOM的。




解決方案 使用 C# 函式

在 ps 5.1 中唯一解法只有呼叫 C# 的函式,這裡有兩個可以用

[IO.File]::WriteAllText("TestFile.txt", "ㄅㄆㄇㄈ`r`n")


另一個是 Lines 區別只是這個會自動換行

[IO.File]::WriteAllLines("TestFile.txt", "ㄅㄆㄇㄈ")


如果要顯式指定編碼的話是這樣設置

$encoding = New-Object System.Text.UTF8Encoding $False
[IO.File]::WriteAllLines("TestFile.txt", "ㄅㄆㄇㄈ", $encoding)


還有另一個 IO.StreamWriter 的流處理函式,不過那寫起來會比較多行有需要自行參考這篇。
https://github.com/hunandy14/cvEncode/blob/master/cvEncoding.ps1#L160




解決方案 使用第三方函式

自己寫的處理函式,主要用於大量轉檔的。

irm bit.ly/cvEncoding|iex
"ㄅㄆㄇㄈ"| WriteContent "README.md" UTF8


有做過效能測試,使用管道行寫入的總時間約略快於用 WriteAllText 一次寫入整份檔案。這結果是蠻奇怪沒繼續深究,但總之速度是可接受的。

https://github.com/hunandy14/cvEncode/tree/master














------------------------以下舊文------------------------


解決方案1 - ASCII

簡單粗暴的的方法,直接寫入 ASCII 就好了。
不能寫中文,但可以記下來應急的時候很好用。

$FileContent = "only english content"
$dstPath = [Environment]::GetFolderPath("Desktop")
$FileContent | Out-File -Encoding ASCII "$dstPath\utf8.txt"

解決方案2 - WriteAllLines

這個方法就不會出問題了,不過是從記憶體一次寫入大小有限。

$FileContent = "中文UTF8內容"
$dstPath = [Environment]::GetFolderPath("Desktop")
$Enc = (New-Object System.Text.UTF8Encoding $False)
[System.IO.File]::WriteAllLines("$dstPath\utf8.txt", $FileContent, $Enc);

解決方案3 - Out-FileUtf8NoBom

這個函式比較能從根本解決問題

載入很簡單,直接打這行就可以把方法載入了

irm https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be/raw/Out-FileUtf8NoBom.ps1 | iex

再來使用方法和原本的 Out-File 差不多 (不過原作者並沒有實現全部的接口)

$FileContent = "中文UTF8內容"
$dstPath = [Environment]::GetFolderPath("Desktop")
$FileContent | Out-FileUtf8NoBom $dstPath\utf8.txt

這樣就能簡單生成一個不帶BOM的UTF8文件了。

原作者:https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be


 

如何安裝 Out-FileUtf8NoBom 到電腦上

上面的指令關掉之後副程式就沒了,每次都要重新載入,如果想要用久的安裝到電腦上,可以使用下面指令。

# 調整權限
Set-ExecutionPolicy RemoteSigned -scope CurrentUser

# 創建初始化腳本檔
if (!(Test-Path -Path $PROFILE )) { New-Item -Type File -Path $PROFILE -Force }

# 從gist載入函式
irm https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be/raw/Out-FileUtf8NoBom.ps1 | iex

# 把函式寫入腳本檔
"`nfunction Out-FileUtf8NoBom {`n${function:Out-FileUtf8NoBom}`n}" | Out-FileUtf8NoBom -Append $PROFILE

這樣就永久安裝到電腦了,隨時都可以用 Out-FileUtf8NoBom 函式了


其他編碼轉換

其他編碼的轉法可以參考這裡的查詢方法

# 直接存取只有預設與UTF8 (可以按TAB一個一個查)
 [Text.Encoding]::Default
[Text.Encoding]::UTF8

# 用名稱查詢
[Text.Encoding]::GetEncoding('UTF-8')
[Text.Encoding]::GetEncoding('BIG5')
[Text.Encoding]::GetEncoding('Shift_JIS')

# 用編號查詢
[Text.Encoding]::GetEncoding(65001)
[Text.Encoding]::GetEncoding(950)
[Text.Encoding]::GetEncoding(932)

實際使用時像這樣

# 編碼
$Enc_Default = [Text.Encoding]::Default
$Enc_UTF8_BOM = [Text.Encoding]::GetEncoding(65001)
$Enc_UTF8 = New-Object System.Text.UTF8Encoding $False
$Enc_BIG5 = [Text.Encoding]::GetEncoding(950)
$Enc_SIFT = [Text.Encoding]::GetEncoding(932)

# 讀寫檔案
$Encoding = $Enc_UTF8
[System.IO.File]::ReadAllLines($Path, $Content, $Encoding);
[System.IO.File]::WriteAllLines($Path, $Content, $Encoding);


UTF8 比較特別要用別的寫法否則會變成帶有BOM的檔案,或者是直接在WriteAllLines() 函數上省略最後面的 $Encoding 參數也可以。



參考

2021年4月22日 星期四

PowerShell 如何把資料夾特定檔案 轉換至 UTF8 編碼

PowerShell 如何把資料夾特定檔案 轉換至 UTF8 編碼

tags: 部落格文章

會寫這篇是因為正在學得教程是中國來的 Eclipse 載入之後直接給我亂碼,要一個檔案慢慢添加個別轉換有夠煩的~

程序也搞了好久,先說 PowerShell5.1 不好用建議乖乖去載 PowerShell7.0來用。

順帶一提要解決 PowerShell5.1 用Out-File輸出帶BOM的問題,把編碼改成 ASCII 或是留空都不要打預設就好了。

本篇範例是用 PowerShell7.0 如果你的環境只能用5.1版,上面提到的在自己測試一下。

更新PS7

懶人包打指令,會自動跳出安裝檔一路下一步到底就好

iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI -Quiet"

或是下面官網手動下載,拖下去之後就有載點了
https://github.com/PowerShell/PowerShell

檔案轉換核心代碼

最關鍵的地方就是這裡啦,有點長我用個變數拆兩段,他是可以合體的。(合體之後記得前面整串$ct要括號起來)

    # 要被轉換的檔案位置與編碼
    $En1=GBK
    $F1='z:\file.txt'
    # 轉換後的檔案位置與編碼
    $En2=UTF8
    $F2='z:\file_UTF8.txt'

    $ct = Get-Content -Encoding $En1 $F1
    $ct | Out-File -Encoding $En2 $F2

這樣執行就可以轉換了,F2可以設置成跟F1 一樣就變成同一份檔案直接轉,但是非常不建議這樣做。不小心連續執行兩次檔案就毀了。

檔案編碼似乎是不能查的,我不知道為什麼用VScode這種近代的編輯器是怎麼偵測的,估計是演算法吧?因為也會有錯的時候所以肯定不是寫在檔案上。

寫成一行的範例

這邊還是要再提一次,當前版本的Windows只有內建的PS版本只到5還沒更新到PS7,PS7要自己手動下載,並且手動打開不會更新到系統上。

範例是把一個在Z:\的檔案 File.java,由 GBK 轉換到 UTF8

(Get-Content -Encoding GBK 'Z:\File.java') | Out-File 'Z:\File_UTF8.java'

要是跳出下面訊息
Get-Content : 無法繫結 ‘Encoding’ 參數。無法將 “GBK” 值轉換為 “Microsoft.PowerShell.Commands.FileSystemCmdletProv
單純只是舊版沒支援GBK而已,去升級吧XD。

批量轉換

這個大概是大家最關心的部分,整個把整資料夾裡面有 java 的全部都抓出來轉換到另一個目錄。

這次第一版半成品,那時候沒查到怎麼處理字串建資料夾的方式有點多此一舉。舊版本就不砍了,有需要功能完善的版本從底下連結自取。

## 初版
function  CovertFileEncoding_basic ($F1, $En1, $En2, $F2){
    $ct = (Get-Content -Encoding $En1 $F1)
    $ct | Out-File -Encoding $En2 -FilePath $F2
}
function  CovertFileEncoding ($F1, $En1, $En2, $F2, $tempPath){
    if (!(Test-Path -Path $tempPath)) {
        Write-Warning "暫存資料夾不存在"
    }
    $F2=$tempPath+$F2
    mkdir $F2 | Out-Null
    Remove-Item $F2
    CovertFileEncoding_basic $F1 $En1 $En2 $F2
}

# 轉換FilePath目錄下的所有java檔案,到tempPath目錄
function CovertDirEncoding($FilePath, $tempPath, $go=0) {
    $list = Get-ChildItem -Path $FilePath -Recurse -Filter *.java
    $listN = Get-ChildItem -Path $FilePath -Recurse -Filter *.java -Name
    for ($i = 0; $i -lt $list.Count; $i++) {
        $F1=$list[$i].FullName
        $F2=$listN[$i]
        if ($go -eq 0) {
            $F2
        } elseif ($go -eq 1){
            CovertFileEncoding $F1 "GBK" "UTF8" $F2 $tempPath
        }
    }
}

$FilePath = 'C:\Users\hunan\Desktop\JavaWeb\25\hotel2\src\'
$tempPath = 'Z:\temp\'
CovertDirEncoding $FilePath $tempPath

完整版

GitGub: https://github.com/hunandy14/DirCoverToUtf8

最底下的部分

# 路徑
$FilePath = "Z:\SourceCode\31"
$TempPath = $PSScriptRoot
cd $PSScriptRoot

預設只要更改 $FilePath 即可

變數 $TempPath 是複製的目的地,預設會自動建立在批次檔旁邊。
最後第三行單純只避免寫代碼的時候出事把目錄調到當前

詳細說明以後再寫,覺得八成還會再大動版本,還沒想到怎麼輸入排除檔案跟轉換檔案的方式。有寫應該會先更新在 github 上。

2021年4月20日 星期二

C++ C 如何獲取命令列的響應後返回的字串

[C/C++] 如何獲取命令列的響應後返回的字串

tags: 部落格文章

這邊以檔案目錄為範例,直接獲取檔案目錄中的字串

核心程式

首先最關鍵的函式是這個,這個可以直接獲取響應的命令

#include <io.h>
vector<string> exec(const string& cmd) {
    FILE* pipe = _popen(cmd.c_str(), "r");
    if (!pipe) throw runtime_error("cmd error");
    vector<string> list;
    for (char buffer[256]=""; !feof(pipe);) {
        if (fgets(buffer, sizeof(buffer), pipe) != NULL and buffer[0]!=0) {
            buffer[strlen(buffer)-1] = 0;
            //cout << buffer << endl;
            list.emplace_back(buffer);
        }
    }
    _pclose(pipe), pipe=nullptr;
    return list;
}

使用範例如下

// 獲取當前目錄文件
auto v = exec("dir /b");
for (auto&& i : v) cout << i << endl;

如此一來就對自己打印出當前目錄的文件了
VS2019預設位置是在該cpp文件的目錄,可以找到cpp文件本身



進階使用範例 - getList

再來是比較進階的,把讀取檔案目錄的cmd代碼整合在一起
寫成簡單易用的函式。預設自動封裝成類別可以直接用

#include <iostream>
#include <vector>
#include <io.h>
#include <direct.h>
using namespace std;
//====================================================
class FileList {
public:
    FileList() = delete;
public:
    // 輸入命令並回存到vector陣列
    static vector<string> exec(const string& cmd) {
        FILE* pipe = _popen(cmd.c_str(), "r");
        if (!pipe) throw runtime_error("cmd error");
        vector<string> list;
        for (char buffer[256]=""; !feof(pipe);) {
            if (fgets(buffer, sizeof(buffer), pipe) != NULL and buffer[0]!=0) {
                buffer[strlen(buffer)-1] = 0;
                //cout << buffer << endl;
                list.emplace_back(buffer);
            }
        }
        _pclose(pipe), pipe=nullptr;
        return list;
    }
    // 獲取清單(檔案+資料夾)
    static vector<string> getList(string path="", string param="") {
        const string sp=" ";
        string cmd = "dir";
        for (size_t pos=0; (pos=path.find("/")) != string::npos;)
            path.replace(pos, 1,"\\");
        auto&& cwd = exec("cd"); // 紀錄初始位置並前往
        if (path != "" and _chdir(path.c_str()))
            throw runtime_error("dir is no exist.");
        if (param == "") // 查詢結果
            param = "/a-s/b/s" + param;
        cmd = cmd + sp + param;
        auto&& result = exec(cmd);
        if (_chdir(cwd[0].c_str())) // 返回初始位置
            throw runtime_error("dir is no exist.");
        return result;
    }
    // 獲取檔案清單
    static vector<string> getFileList(const string& path, const string& param="") {
        return getList(path, "/a-d-s/b/s/on " + param);
    }
    // 獲取資料夾清單
    static vector<string> getDirList(const string& path, const string& param="") {
        return getList(path, "/ad-s/b/s/on " + param);
    }

};

void test_exec(const string& path) {
    cout << "------------------test_exec-------------------" << endl;
    if (path != "" and _chdir(path.c_str())) {
        throw runtime_error("dir is no exist.");
    } 
    auto v = FileList::exec("dir /a-d/b/s/on *.*");
    for (auto&& i : v) {
        cout << i << endl;
    }
    cout << "---------------------------------------------" << endl;
}
void test_getList(const string& dir) {
    vector<string> v;
    // 第一個參數是路徑,留空預設是當前工作目錄
    cout << "----------------test_getList-----------------" << endl;
    v = FileList::getList(dir);                    // 獲取檔案清單(資料+檔案)
    for (auto&& i : v) cout << i << endl;
    cout << "---------------------------------------------" << endl;
    v = FileList::getFileList(dir);            // 獲取檔案(不包含資料夾)
    for (auto&& i : v) cout << i << endl;
    cout << "---------------------------------------------" << endl;
    v = FileList::getDirList(dir);                // 獲取資料夾(不包含檔案)
    for (auto&& i : v) cout << i << endl;
    cout << "---------------------------------------------" << endl;
    v = FileList::getFileList(dir, "*.txt");    // 獲取特定檔案
    for (auto&& i : v) cout << i << endl;
    cout << "---------------------------------------------" << endl;
}
//============================================================================
int main(int argc, char const* argv[]) {
    const char* dir0 = "";
    const char* dir = "Z:\\a";
    test_exec(dir);
    test_getList(dir);
    return 0;
}
//============================================================================

2021年4月19日 星期一

Win10 不開機 EFI 開機檔損毀如何修復

Win10 不開機 EFI 開機檔損毀如何修復

通常症狀會出現以下錯誤信息

reboot and select proper boot device

不過這個信息導致的原因有很多,這邊只是說明其中的一個,開機檔損毀怎麼修復。


步驟:
1. 插入Win10 安裝USB
2. 然後按修復
3. 移除USB重新啟動


2021/09/23
後來看到有人提才想起來,內建修復修是包含開機磁區修復的。當初寫這篇的時候沒想到,底下是舊文,想自己折騰一下如何手動修復可以看看。

修復沒修好的話估計是有其他狀況了,具體情況要實際看看才知道。然後內建自帶的是修復開機磁區,並不會重建。如果你是拔掉其他硬碟導致不開機,八成是當初安裝的時候沒拔舊硬碟,導致開機磁區在舊硬碟上,這種情況在新硬碟上只能自己手動重建,參考底下文章說明。

修復的邏輯是先找同一顆硬碟上的EFI分區,沒有的話就找別的硬碟。為什麼沒拔舊硬碟會造成這個狀況,是因為在創建分區的時候如果已經存在EFI分區(包含其他硬碟)則不會自動建立,然後安裝完成後會自動執行一次修復,開機磁區就裝到其他硬碟上了。




重建EFI

重建EFI有很多方式,用軟體是最快的,一個按鍵就修好了
不過相對也麻煩還要多載一個軟體下來。
如果不想抓軟體也可以試試下面還有用命令修復的方法。


備註:有一種情況是連EFI分區都沒有。通常當初安裝Windows的時候沒把舊硬碟拔掉,導致啟動分區在舊硬碟上,新硬碟壓根就沒有。這種情況先跳到最後面看。


使用軟體修復EFI

首先你得找到另一台電腦,把硬碟拔過去插著用Win10開機
然後載這個軟體 Dism++:https://www.chuyu.me/zh-Hant/

再來方法很簡單,打開之後先選要修復的槽位,點一下
(你插到別台電腦就一定不是C曹歐,不要被我的是意圖干擾了)


然後選擇修復


OK修好了。

底下是另一個用命令修復的方法,這邊如果修好了就別再往下做了。





替EFI磁碟新增磁碟代號

首先你得找到另一台電腦,把硬碟拔過去插著用Win10開機。或者是利用隨身碟安裝PE插進壞掉的目標電腦然後開機用命令修復。

開準備好環境之後就可以開始拉,滑鼠移動到開始,對著微軟旗幟按右鍵,打開Powershell


打開之後輸入

DISKPART

再來會進入該程序,接著輸入

list disk

這個步驟是查詢你的硬碟在哪個曹
以我的圖來說我要修的硬碟是894GB的那顆,就選那顆對照前面編號

sel Disk 1

接頭要查詢 EFI 分區在哪裡

list part

EFI 分區他的 Type 處會寫著 Systme 選擇那個分區
以我的圖來說在分區2 (通常會在分區1)

sel Parttition 2

再來就可以替他新增磁碟機代號了

assign letter=Y




最後記得離開 DiskPart 
exit






修復EFI分區

好了之後就可以繼續下一步修復分區了,只剩一行指令了。
重新開一個新的 Powershell

然後輸入

# E = 目標硬碟的系統槽, Y = 目標硬碟的EFI槽
bcdboot E:\windows /f UEFI /s Y:\ /l zh-tw

上面記得槽位要選對,不確定的去我的電腦裡面看一下確認,然後就修好瞜~


然後你可以回到剛剛讓你留著的視窗重新進入 diskpart 後輸入

# 取消掛載的EFI磁碟代號
remove letter=Y





新增EFI分區

如果你是連EFI分區都沒有的,整個硬碟只有一個曹上面指令會做不了,要先把EFI分區弄出來。

dikspart

# 查看硬碟
list disk

# 選擇目標硬碟
sel disk 1

# 查看分區
list part

# 選擇分區
sel Partition 1

# 壓縮該分區
shrink disired=300

# 創建EFI分區
create partition efi size=100

# 格式化EFI分區
format quick fs=fat32 label="System"

# 指定EFI分區磁碟代號
assign letter=Y

再來接著"修復EFI分區"的標題那部分即可