2021年5月12日 星期三

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

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

tags: 部落格文章

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

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


如何任意改變檔案的時間可以參考這篇文
CHG: 2025-02-04 PowerShell 修改檔案日期與時間 (3)




建立日期 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;
}
//============================================================================