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;
}
//============================================================================

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分區"的標題那部分即可