2021年5月23日 星期日

寫C/C++測試題目時如何讀取測資

寫C/C++測試題目時如何讀取測資

tags: 部落格文章

以前寫測試題目的時候最麻煩的第一步就是如何讀測資了

方法有很多cin/cout是最簡單的,但是相對的會比較費時,建議是盡量避開全部都使用 printf 和 scanf。

再來第二個問題是,在本地測試的時候會把測資寫到檔案裡面讀取,但是上傳之後是直接從stdin輸入,導致上傳的時候還要修改一下,

這邊也一併提出解決辦法,整理一下我認為最佳的解法。

說明

先說明如何讀取的代碼

char buff[32];
while (scanf("%s", buff)!=EOF) {
    printf("%s\n", buff);
}

直接用一個while包起來,代碼從裡面開始寫就可以了。

再來是解決上傳修改代碼的問題使用這個函式,可以直接修改scanf讀取檔案,而不是從stdin讀取,這樣代碼就不用修改了

freopen("input.txt", "r", stdin);   // scanf從檔案讀取
freopen("output.txt", "w", stdout); // printf 輸出到檔案

因為函式會略微影響效能,應該有不少人是用define去包起來的

#define ONLINE_JUDGE

#ifndef ONLINE_JUDGE
    if(in) freopen("input.txt", "r", stdin);
    if(out) freopen("output.txt", "w", stdout);
#endif // ONLINE_JUDGE

如此一來只要註解掉第一行的 define 中間代碼就不會被執行了
不過這邊推薦一個更好的方式,用 inline 函式,效果跟 define 是一樣的

inline void debugMode() {
    if(in) freopen("input.txt", "r", stdin);
    if(out) freopen("output.txt", "w", stdout);
}

加上 inline 的意思是,如果函式符合一定的規則(簡單說就是足夠簡單),編譯器會自動把函式打掉,直接開到呼叫的地方。就等於沒有呼叫函式的意思了,所以速度會快一點。

如果有其他需要寫到函式建議都要加上 inline ,可以省下一點點時間。

完整的範例

#include <cstdio>
#include <cstdlib>

inline void debugMode(const char* in, const char* out) {
    if(in) freopen(in, "r", stdin);
    if(out) freopen(out, "w", stdout);
}

int main(int argc,char *argv[]) {
    debugMode("in.txt", NULL); // 輸出保持在終端機

    for (char buff[32]; scanf("%s", buff)!=EOF; ) {
        printf("%s\n", buff);
    }
}

參考

 

2021年5月19日 星期三

C/C++ 依照特定格式 讀取檔案並 切割字串

C/C++ 依照特定格式 讀取檔案並 切割字串

tags: 部落格文章

這個新手大概很常用到,每次要用都google半天。這篇是把這些常用的函式封裝成一個物件,直接解決痛點方便操作。

因為是用 C++ 17 寫的,刷題提交用的代碼可能用不上,網站未必支援。簡單的讀檔可以參考這邊兩篇站內文。

讀取檔案

讀檔的問題參照上面兩篇就可以解決,這邊就概略貼上代碼就好

// 逐行讀取
vector<string> ReadFile_line(const string file_name) {
    vector<string> v;
    fstream fs(file_name, ios::in);
    if (!fs.is_open())
        throw runtime_error("Reading error.");
    for (string str; getline(fs, str);)
        v.emplace_back(str);
    return v;
}



切割字串

傳統的C字串處理可以這樣寫

#include <string.h>
void stringSplit(char* str, const char* delim) {
    char temp[64];
    strcpy(temp, str);

    char* pch = strtok(temp, delim);
    while (pch != NULL) {
        printf("%s\n", pch);
        pch = strtok(NULL, delim);
    }
}

C++ 11 可以這樣寫

#include <string>
void split(const string& s, vector<string>& tokens,
    const string& delimiters = " ")
{
    string::size_type lastPos = s.find_first_not_of(delimiters, 0);
    string::size_type pos = s.find_first_of(delimiters, lastPos);
    while (string::npos != pos || string::npos != lastPos) {
        tokens.push_back(s.substr(lastPos, pos - lastPos));
        lastPos = s.find_first_not_of(delimiters, pos);
        pos = s.find_first_of(delimiters, lastPos);
    }
}

C++17 可以更有效率的這樣寫

#include <string_view>
vector<string_view> splitSV(string_view strv, string_view delims = " ") {
    vector<string_view> output;
    for (size_t first = 0; first < strv.size();) {
        const auto second = strv.find_first_of(delims, first);
        if (first != second)
            output.emplace_back(strv.substr(first, second - first));
        if (second == string_view::npos)
            break;
        first = second + 1;
    }
    return output;
}

透過這樣的方式,就能夠把字串依照特定格式給切出來了。



封裝物件

切割完畢之後就用封裝拉,一般就是用 vector<string> 封裝,想要效率更高一點可以使用 vector<string_view>

因為有點長就貼到gist上了

封裝好的物件怎麼使用有一併寫在檔案底下的test函式裡

下面就簡單說明一下,最簡單的切割

string str = "123 | 321";
OneLine line(str, " | ");
cout << line << endl;

vector<string_view>&& tokenList = line;
for (size_t i = 0; i < tokenList.size(); i++)
    cout << tokenList[i] << endl;

物件本身返回的是一個 vector
這就意味了著在for裡面可以透過 line[idx] 來讀取被切割的字串

如果要從檔案讀取可以使用 openFile()

OneLine line;
line.openFile("a.txt");
while (line.getline()) {
    vector<string_view>&& tokenList = line;
    cout << tokenList << endl;
}

此時獲取的 tokenList 就可以用下標取出個別的字串了。



參考

Win10/11 變更使用者目錄的路徑 (中文改英文)

Win10/11 變更使用者目錄的路徑 (中文改英文)

tags: 部落格文章

怕過程出錯影響到使用者資料,可以先先創建第二個中文姓名的使用者充當白老鼠試驗,避免出什麼狀況。這不是必須的,不想弄第二次就直接用本尊來吧。

創建好之後記得先登入一次,不然使用者資料是空的還沒初始化




獲取使用者UID

先用要更改的使用者,打開終端機輸入以下命令獲取UID信息
(打開終端機方法:按下 Win+X 然後再按 A。或是直接搜尋Powershell打開)

[Security.Principal.WindowsIdentity]::GetCurrent().User.Value

會得到一串數字,把結尾幾位數記下來等一下會用到。




登入第二個管理員使用者

同一個帳號無法在檔案執行中修改目錄名稱,需要第二個普通管理員使用者來操作。如果電腦上沒有第二個使用者可以暫時打開超級管理,設定完後要刪除比較方便。


1. 打開超級管理員

先按下 Win+X 然後再按下 A ➔ 打開終端機,接著再輸入以下的代碼

net user "Administrator" /active:yes

2. 然後重新啟動電腦,選擇使用Administor(超級管理員)帳號登入



變更帳號路徑

再來進入超級管理員帳號就有權限可以做事情了


1. 打開登錄檔編輯器 (在開始搜尋 regedit 可以找到)



2. 找到下面這條路徑 (可以像資料夾那樣直接貼在上方快速到達)

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Profilelist




3. 修改登錄檔與資料夾名稱

這邊會看到很多資料夾 S-1-XXXX 這種的

  • 其中一個是你的使用者文件,剛剛有把數字記下來就直接對照就好
  • 沒有記下來就一個一個點進去,點到有熟悉的中文路徑就是對了


裡面可以看到一個文件是 ProfileImagePath 這個就是資料夾所在位置了。

  1. 雙擊 ProfileImagePath 之後複製上面的值(路徑)
  2. 打開資料夾,上方貼上[1]的路徑後,按ENTER前往
  3. 回上一層,變更使用者文件的資料夾名字
  4. 雙擊進入變更後的使用者文件,並從上方複製路徑
  5. 回登錄檔管理器,把變更後的路徑貼回 ProfileImagePath 上

打錯會出事,千萬不要手打用複製的。



4. 確保萬無一失再重啟

重開前務必確保以下項目

  1. 使用者資料夾改名了
  2. 登錄檔 ProfileImagePath 的數值改了
  3. 上面兩個路徑是吻合的
再來重新啟動電腦並登入修改完畢的帳號就完成了



5. 關閉超級管理員

確保能正常登入後就可以把超級管理員關掉了,執行以下的代碼關閉。
net user "Administrator" /active:no







2021年5月12日 星期三

PowerShell 修改檔案的 建立時間/修改時間/存取時間 程式

PowerShell 修改檔案的 建立時間/修改時間/存取時間 程式

tags: 部落格文章


新文章有更易用的方式詳細請看這篇https://charlottehong.blogspot.com/2022/02/powershell.html







關於檔案的 修改時間 和 存取時間有什麼差別可以看這篇站內文
https://charlottehong.blogspot.com/2021/05/win10.html

字串格式和日期格式怎麼互轉可以參考這一邊
https://charlottehong.blogspot.com/2021/05/powershell-string-datetime.html

修改檔案時間

其實也就是讀檔然後修改而已。

$Date = New-Object DateTime(2021, 12, 31, 18, 30, 59);
$FileName = "Z:\a.txt"

$file = Get-Item $FileName
$file.CreationTime   = $Date; #建立日期
$file.LastWriteTime  = $Date; #修改日期
$file.LastAccessTime = $Date; #存取日期

懶人包程式

這是封裝好的程式,要使用的話按下 Win+X 在按 A 會跳出一個可以輸入的視窗,把底下全部貼上。

# 產生日期格式
function NewDatetime {
    param (
        [string]$date = "",
        [string]$formatType = "yyyy-MM-dd HH:mm:ss"
    )
    # 未輸入日期則返回當前時間
    if ([string]::IsNullOrEmpty($date)) {
        $newDate = (Get-Date)
    }
    # 格式化日期
    else {
        $newDate = [DateTime]::ParseExact(
            $date.Trim(), $formatType,
            [CultureInfo]::InvariantCulture
        )
    }
    return $newDate
}
# 讀取檔案日期
function readFileDate {
    param (
        [string]$FileName
    )
    $file = Get-Item $FileName;
    Write-Host "建立日期" $file.CreationTime;     #建立日期
    Write-Host "修改日期" $file.LastWriteTime;    #修改日期
    Write-Host "存取日期" $file.LastAccessTime;   #存取日期
}
# 改變檔案日期
function ChangeFileDate {
    param (
        # [Parameter(Mandatory, Position=0)]
        [Parameter(Mandatory, Position=0)]
            [string]$FileName,
        [Parameter(ParameterSetName="setDetail")]
            [datetime]$CreationTime,
        [Parameter(ParameterSetName="setDetail")]
            [datetime]$LastWriteTime,
        [Parameter(ParameterSetName="setDetail")]
            [datetime]$LastAccessTime,
        [Parameter(ParameterSetName="setAll")]
            [datetime]$AllTime,
        [switch]$Preview
    )
    if ($AllTime) {
        $CreationTime   = $AllTime;
        $LastWriteTime  = $AllTime;
        $LastAccessTime = $AllTime;
    }

    $file = Get-Item $FileName
    if ($Preview) {
        Write-Host "[$FileName]"
        Write-Host "  " $file.CreationTime "-->" $CreationTime
        Write-Host "  " $file.LastWriteTime "-->" $LastWriteTime
        Write-Host  "  " $file.LastAccessTime "-->" $LastAccessTime
    } else {
        if ($CreationTime) {
            $file.CreationTime = $CreationTime; #建立日期
        } if ($LastWriteTime) {
            $file.LastWriteTime = $LastWriteTime; #修改日期
        } if ($LastAccessTime) {
            $file.LastAccessTime = $LastAccessTime; #存取日期
        }
    }
}

全部貼上去之後按 Enter 就可以開始使用了



使用方法

在使用前先執行這兩行代碼(記得要按Ennter輸入)
會自動切換到桌面,並建一個檔案等一下要範例用

cd ~\Desktop
"" >> a.txt

好了就可以開始拉

查詢檔案日期

readFileDate a.txt

修改全部日期

ChangeFileDate "a.txt" -AllTime "2021-5-10 23:59:59"

改完之後按 ↑ 兩次選到剛剛執行查詢的代碼,可以看一下有沒有改變

修改單一日期

如果只想改變其中一個而已的話,改法像這樣

# 只改變建立日期
ChangeFileDate "a.txt" -CreationTime "2021-5-11 23:59:59"
# 只改變修改日期
ChangeFileDate "a.txt" -LastWriteTime "2021-5-12 23:59:59"
# 只改變存取日期
ChangeFileDate "a.txt" -LastAccessTime "2021-5-13 23:59:59"

如果要改其中兩個,可以打兩行也可以把後面的部分串起來就好

# 改任意兩個(這3組可以隨便串)
ChangeFileDate "a.txt" -CreationTime "2021-5-14 23:59:59" -LastAccessTime "2021-5-15 23:59:59"

預覽

如果你怕改錯的話可以在加上 -Preview 預覽一下
加上這個之後就不會變更日期,只會看到預覽的訊息

ChangeFileDate "a.txt" -AllTime "2021-5-10 23:59:59" -Preview

PowerShell 字串String 與 日期Datetime 格式轉換

PowerShell 字串String 與 日期Datetime 格式轉換

tags: 部落格文章

字串轉時間

建構子

直接用建構子是最簡單的

New-Object DateTime(2021, 5, 12, 22, 29, 30)


進階自訂格式

核心的程式長這樣

[DateTime]::ParseExact("13.May.2017", "dd.MMM.yyyy",[CultureInfo]::InvariantCulture)

這樣簡單一行就能夠格式化字串到日期了

強化一下寫好的副程式是這樣的

function newDatetime {
    param (
        [string]$date = "",
        [string]$formatType = "yyyy-MM-dd HH:mm:ss"
    )
    # 未輸入日期則返回當前時間
    if ([string]::IsNullOrEmpty($date)) {
        $newDate = (Get-Date)
    }
    # 格式化日期
    else {
        $newDate = [DateTime]::ParseExact(
            $date.Trim(), $formatType,
            [CultureInfo]::InvariantCulture
        )
    }
    return $newDate
}

使用的話直接這就可以用了

newDatetime "2000-01-01 12:00:00"

要自訂格式的話,後面空格多補一個參數

newDatetime "13.May.2017" "dd.MMM.yyyy"



日期轉字串

再來是把日期轉出來到字串,這個就比較容易

方式1

直接轉成字串即可

(Get-Date).ToString("yyyy-MM-dd HH:mm:ss")

方式2

陣列轉換

'{0:yyyy-MM-dd HH:mm:ss}' -f (Get-Date)

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 了