跳到主要內容

發表文章

目前顯示的是 2020的文章

[C++] 用 stringstream 分割同時包含 ASCII 和 Unicode 中文的字串

假設有一個檔案,內容包含中文,也包含 ASCII 字元(皆是可顯示字元)。 如果這個檔案格式為 UTF-8,則 ASCII 碼佔用 1 個 byte,中文字佔用 3 個 byte。且代表中文的這 3 個 byte,必定是負值。 (其他語言的文字,未必是用 3 byte 儲存,但繁體中文佔用了 3 byte,UTF-8 的特性就是 byte 數是變動的) 所以 stringstream 用 UTF-8 的 ASCII 的可顯示字元,來當作 delimiter分割字串,是很安全的,例如用 ASCII 的 "空白" 來將以下的檔案,每一行輸入都拆解成兩個獨立字串: apple 蘋果 橘子 哈哈 兔子 rabbit 可以這樣寫: ifstream inputFile(fileName.c_str());  string a, b, line; while(getline(inputFile, line)) {     stringstream tkn(line);     tkn >> a >> b;         cout << " [Debug] a == " << a << endl;     cout << " [Debug] b == " << b << endl; } inputFile.close(); 輸出會是: [Debug] a == apple [Debug] b == 蘋果 [Debug] a == 橘子 [Debug] b == 哈哈 [Debug] a == 兔子 [Debug] b == rabbit 一般我很少直接採用 Unicode 編碼來處理字串,較常採用 UTF-8 。因為 UTF-8 的前 128 個編碼,跟 ASCII 編碼是完全相同的,如此方便程式碼可以相容 ASCII 編碼,同時又可以處理寬字元。許多支持多國語言的程式,預設都用 UTF-8 就是這個緣故。 注意在 Window 系統記事本若將檔案存成 UTF-8 編碼,檔頭會多了 0xefbbbf 這 3 個 b

[Code Daily] 2020/06/18 單字記憶遊戲(一)

今天要開始實作一個小遊戲:單字記憶遊戲。 遊戲的規則,就是有許多蓋著的牌,每張牌有一個英文單字,或者某個英文單字的中文翻譯。 玩家僅能翻開牌觀看單字幾秒,牌會自動蓋上。必須將單字牌跟對應的翻譯牌在短時間內同時掀開,才能得到分數。 先從最簡單的功能開始實作:如何用滑鼠觸發事件,把牌掀開,並顯示單字。 程式語言採用 C++ 搭配 SDL2。 工作環境:elementary OS. 1.     首先我想要在某個位置 rendering true type font,這就需要安裝 SDL_ttf.h: sudo apt-get install libsdl2-ttf-dev g++ compile 時記得要 link: -lSDL2_ttf 2.      到 fontsquirrel 可以下載免費的 ttf 字型。我選擇的字型是 "Raleway"。 接下來分成三個步驟: initialized SDL_Renderer。 讀 Texture 圖檔,讀 ttf 檔案。把所有牌的可能圖案都先畫到一個 SDL_Texture 內。 滑鼠觸發事件處理。繪圖。 重點在第三個步驟 滑鼠觸發了某個按鈕,按鈕物件就會更新自己的狀態,例如將狀態更新為 Botton UP/Down,然後根據這個狀態值,去將 SDL_Texture 裁切出要顯示的部份,把切下來的部份 render 到一個 SDL_Renderer 物件上。 明天繼續完成細節的部份。

[C++] 計算程式執行時間:clock 和 time 函式

要計算一段程式碼執行時間,可以用 time 函式: 1 2 3 4 5 time_t start, end; start = time(NULL); // DO SOMETHING... end = time(NULL); cout << " [Msg] total time: " << difftime(end, start) << " sec" << endl; time 函式的精確度只到達秒,如果要精確到毫秒等級,要用 clock 函式: 1 2 3 4 5 6 clock_t start, end; start = clock(); // DO SOMETHING... end = clock(); cout << ( double )(finish - start) / CLOCKS_PER_SEC << endl;

[開箱] Filco Majestouch 2 忍者鍵盤第二代

最近買了一個新款的 Filco 忍者鍵盤第二代,NINJA Majestouch 2: 鍵盤對我來說蠻重要的,以前就很喜歡 Filco 品牌的鍵盤,我大學使用的第一支茶軸鍵盤就是 Filco 的牌子,現在已經充滿了灰塵,使用了至少已有 8 年了吧: 畢業後有買另一個 irock 鍵盤: 這禮拜拆開新鍵盤來看看: 內部包裝 內部包裝 採用按鍵的文字採用側印 鍵帽與拔鍵器 整體外觀 整體而言,美感簡約低調,按鍵的反饋力也很好,是一個適合寫程式的鍵盤。 相較於我的另一個也是德國櫻桃茶軸鍵盤 irock 品牌,我覺得 Filco 的按鍵回饋比較綿密,有漸層的回饋感,且按鍵聲音比較安靜。irock 則比較像機械打字的回饋感,敲字時也都蠻舒壓。

[心得] 復古、老派的程式設計之路

最近看到 John Carmack 2018 年的貼文中的幾段話: I’m not a Unix geek.  I get around ok, but I am most comfortable developing in Visual Studio on Windows.  I thought a week of full immersion work in the old school Unix style would be interesting, even if it meant working at a slower pace.  It was sort of an adventure in retro computing — this was fvwm and vi.  Not vim, actual BSD vi. In the end, I didn’t really explore the system all that much, with 95% of my time in just the basic vi / make / gdb operations.  I appreciated the good man pages, as I tried to do everything within the self contained system, without resorting to internet searches.  Seeing references to 30+ year old things like Tektronix terminals was amusing. In the spirit of my retro theme, I had printed out several of Yann LeCun’s old papers and was considering doing everything completely off line, as if I was actually in a mountain cabin somewhere, but I wound up watching a lot of the Stanford CS231N lectures on YouTube, and found them

[Concept] 二補數(2's Complement)除法跟模數的意義

計算機領域,負數常用二補數表示。 很多計算機書籍有介紹二補數,不再贅述。 這裡討論的是:把一個二補數,除以一個數 K,或對 K 取 Mod,得到的答案意義為何? 首先,二補數可視為 2^N ( 2 的 N 次方)減一純量,N 視為無窮大。 例如:-3 二補數表示法為:2^N - 3 二進位表示:(100000000...) - (11) 將 2^N 除以 K 取商,餘數丟棄,K 是 2 的冪次方(2, 4, 8, 16...),此商在數線上意義為何? 把 2^N 看成無窮多個班級所組成的人數,每班 K 人。由於 K 是 2 的冪次方,所以 2^N 人剛好能排滿 n 個班級, n 剛好也是 2 的冪次方 :  K  K  K  K   ...   K   K    做以下觀察: 任意正數 P ,(2^N - P) 是 -P 的二補數表示法。 (2^N - P) / K = n - Q = 2^某次方 - Q,是 Q 的二補數。 Q = ceiling(P/K),也就是假如 P/K = 3.45,那麼 Q 就是 3.45 進位 = 4。 所以 2^某次方 - Q 就是 - ceiling(P/K) 的二補數表示。 (2^N - P) % K,也就是取模數,為 K - (P%K) = 1。 結論: 二補數除以 K,K 為 2 的冪次方,商還是二補數。 二補數 Mod K,即取模數,K 為 2 的冪次方,得到的結果,是一個純量,這個純量不是二補數。 二補數除以 K,K 為 2 的冪次方,跟正數除以 K 的意義有一樣的地方,算出的商為一個編號,代表第幾個班級,班級的編號從 0 開始。 例如:P = 第 0, 1, 2, ... , K-1 個同學,被編在編號為 P/K = 0 的班級,P = 第 K+0, K+1, K+2, ... , 2K-1 被編在編號為 P/K = 1 的班級。 同理,P = -1, -2, -3, ... , -K 的同學,屬於編號為二補數 (-P)/K = -1 的班級,-K-1, -K-2, -K-3, ... , -2K 屬於在編號為 -2 的班級。 二補數 (-P) % K,跟正數 P % K 行為意義有一樣的地方。 假設 K = 8 ,即每 8 個人一班,每班學號從 0 開始到 7,假設 P 等於 15, P/K = 1,P % K = 1

[Git] Bitbucket 建立 Repository Using Git

以下介紹用 bitbucket 搭配 git 控管程式碼的快速上手。 首先在 bitbucket 建立新的 Repository。申請完畢後,假設 Repository 的名稱叫做 test,可以按 bitbucket 網頁上的 Clone 按鈕,會出現 git clone 指令,通常為: git clone https://<user_name>@bitbucket.org/<user_name>/test.git <user_name> 那裡寫的是你的 bitbucket 帳號名稱。 clone 完畢,本地端出現 test 資料夾。 將要新增的檔案,用以下指令 add 到 repo 內: git add <file_name> 然後 commit: git commit -m "這裡打註解" 最後 push 上 code,就會正式將本地端檔案上傳 bitbucket: git push origin master 大功告成!

[Python] 輸出二進位檔案

記錄一下 python 如何輸出成二進位檔案。 我輸出二進位檔案的方法,採用的是 bytearray 這個函式。 我會先把我要輸出的數字,以 byte 為單位,先放入一個 list 陣列,然後將 list 陣列用 bytearray 轉成 byte 格式,寫入檔案: #!/usr/bin/env python3 import struct def main (): outputFile = open( "tmp.bin" , "wb" ) byte_dat = [ 0xab , 0xcd , 0xef ] binary_pack = bytearray(byte_dat) outputFile.write(binary_pack) outputFile.close() if (__name__ == "__main__" ): main() 我們用 Linux 的 xxd 指令 一個一個 byte 看一下輸出的檔案長什麼樣子: 在終端機輸入: xxd -g1 -c1 ./tmp.bin 輸出結果: 00000000: ab  . 00000001: cd  . 00000002: ef  . 可知第一個 byte 是放在陣列 index 0 的位置,第二個 byte 是放在陣列 index 1 的位置,餘類推。

[Linux] 安裝 conda 並用 conda 安裝套件

本篇文章介紹 conda 在 Linux 安裝與基本使用方法。 conda 是一個套件包管理器,跟 apt-get 一樣。conda 的宗旨最初是為了管理複雜的 Python 語言包安裝,後來開始支援其它語言包的安裝(例如 R 語言)。 在 Linux 安裝 conda 指令無法採用 apt-get 指令,要安裝 conda 有兩種方式: 安裝 Anaconda。Anaconda 是一個 Python 的發行版,專門用於計算科學,內建很多的預設數據科學的軟體包,因此會安裝 Anaconda 會需要較大的硬碟空間,會安裝約 3 GB 大的檔案到電腦內。 安裝 Miniconda,是最小安裝版本的 Anaconda,內建 conda、Python 和一些基本套件和基本工具。我目前是安裝這個,因為我不想要安裝一些目前還用不到的語言包。 Minicoda 可以在 這個網站 下載。 我選擇 Python 2.7 的 Linux 64-bit 版本下載,安裝過程 不需要使用 sudo 權限 ,否則之後 conda 執行上會有權限問題,conda 在執行的時候是無法使用 sudo conda ... 來執行指令的。 安裝過程如下(作業系統為 Linux 發行版:Elementary OS): 執行 (不需要加 sudo)  bash ./Miniconda2-latest-Linux-x86_64.sh 會出現歡迎畫面: Welcome to Miniconda2 4.7.12 In order to continue the installation process, please review the license agreement. Please, press ENTER to continue >>> 按 Enter 後,閱讀完授權合約,輸入 yes 接受合約條款。 Do you accept the license terms? [yes|no] [no] >>> 預設安裝路徑是家目錄的 minicoda2,按下 Enter 可即刻安裝。 Miniconda2 will now be installed into this location

[C++] 電影駭客任務 Matrix 文字效果

1999 年上映的電影駭客任務(Matrix)於明年要推出第四集了。這部電影不論在哲學觀以及拍攝效果,在當年的影史上都是一個突破。 其中這部電影中最著名的電腦執行畫面深植在多少技術愛好者的心中: 本篇文章記錄我如何用 C++ 實作這種電影 matrix 文字下雨(raining)效果,這種文字效果有很多種作法,可以做得很精緻,我採取比較簡單的一種實現方法: 首先程式的邏輯從如何刷新 M x N 的文字 random 陣列開始。 假設有一個 M x N 的 random 文字陣列,我們可以從第 0 個 row 開始刷新每個字元。 假設現在正在處理第 0 個 row,我們走訪每個字元,然後隨機挑選某幾個文字更新,更新的方法有將當前字元用空白字元替換,或用任意非空白字元替換兩種情況。 當第 0 個 row 更新完之後,就刷新螢幕,然後繼續更新下一條 row,也就是更新第 1 個 row。 重點來了。 刷新螢幕的方法,順序為: 先清空螢幕 先顯示最新刷新的 row。 依序顯示之前刷新的 row。 原始碼如下: // C++ program for implementation of falling matrix effect #include<iostream> #include<string> #include<thread> #include<cstdlib> #include<ctime> #include<chrono> const int cols = 60 ; const int rows = 30 ; const int switch_num = 5 ; const int delay = 90 ; char screen[rows][cols]; using namespace std; int main ( int argc, char ** argv) { srand(time(NULL)); bool status[cols] = { 0 };

[C++] CMake 建置 C/C++ 程式

在編譯程式的時候,會設定一些組態(configuration,簡稱 config)。 這些組態包含我們的編譯過程需要用哪一種編譯器,哪一些函式庫,要編譯哪些檔案和編譯的指令等等。 以 Linux 環境而言(Windows 系統的操作也大同小異,原理都是一樣的),在編譯 C++ 專案時,上述這些組態可以寫在一個叫做 Makefile 的檔案裡。在 Makefile 檔案所在資料夾執行 make 指令,make 這隻程式,就會吃進 Makefile 並根據 Makefile 內的組態設定做出對應編譯的動作。 通常這個 Makefile 寫起來很繁瑣,因此才會有 cmake 這個工具的誕生。 CMake 是一個能自動幫我們產生 Makefile 的程式。 CMake 基本的觀念就是,我們只要指定好編譯器種類,以及我們要編譯的目錄、執行檔名稱等等設定,CMake 就會產生 Makefile。 CMake 基本使用方式,假如我們有個資料夾結構如下: . ├── build ├── CMakeLists.txt ├── lib ├── reference │   └── matrix_effect.cpp └── src └── meffect.cpp 這種資料夾結構是我習慣的 hierarchy。用這樣的資料夾做管理,專案比較不會凌亂。 src 資料夾裡面放 .cpp 程式碼。 lib 資料夾放程式碼用到的外部函式庫,即 include 的 header file。 而程式碼編譯成執行檔的動作,則在 build 資料夾內操作。 reference 資料夾內則放一些參考資料跟文件。 build 資料夾內有個 CMakeLists.txt,這個檔案就是 cmake 指令需要的檔案,以這個專案資料夾結構為例,CMakeLists.txt 內容可以這樣寫: cmake_minimum_required( VERSION 2.8.9 ) project( meffect ) include_directories( lib ) file( GLOB SOURCES "src/*.cpp" ) add_executable( meffect ${ SOURCES }) 然後在 build

[Linux] 用 dd 指令燒錄 usb 開機碟

環境:elementary os 目標:把預燒錄的 iso 檔案燒錄到 usb 隨身碟內,做成開機磁碟。 用 lsblk 指令看隨身碟掛載在哪個位置。(通常掛載在 /media/... 路徑內,或是用視窗的檔案瀏覽器觀看顯示的 usb 磁碟名稱,再看 /media/ 下哪一個磁碟名稱跟 usb 隨身碟同名) lsblk 顯示的畫面會類似: sdc          8:32   1   7.1G  0 disk └─sdc1   8:33   1   7.1G  0 part /media/<這裡會顯示 usb 路徑及名稱> 下指令卸載隨身碟: umount /media/<usb 路徑及名稱> 然後再次用 lsblk 檢查是否卸載成功。 用 sudo 權限下 dd 指令燒錄: sudo dd if=<path_of_iso_file>.iso of=/dev/sdc bs=1M bs=1M 意思是一次要寫入幾個 byte,官方說明寫著: bs=BYTE:  read and write up to BYTES bytes at a time (default: 512); overrides ibs and obs. 燒錄結束時,會顯示類似如下的 report: 輸入 1419+1 個紀錄 輸出 1419+1 個紀錄 1488797696 bytes (1.5 GB, 1.4 GiB) copied, 831.393 s, 1.8 MB/s

[Training] UVA 1595 Symmetry

本題為 Brute Force 可以迅速解題。 題目給予一些二維座標,求是否所有點左右對稱。 看似簡單,但需要注意以下幾點,不然很容易拖掉 Debug 時間。 1.   scanf 讀取輸入的時候,遇到自定義的 struct,不必遲疑,直接把 “&” 寫在前面:           scanf("%d %d", &a[i].x, &a[i].y); 2.   取 x 最大值和最小值,相加當作中點,不需要除以 2,避免出現浮點數問題。 3.   x_min 和 x_max 取完,要馬上回歸初始值。x_min 初始值要是很大的值,x_max 初始值是很小的值。 4.   要考慮座標有可能落在對稱線上,for 迴圈掃過所有點,某一點自己也可以跟自己取中點來做判斷。 完整程式碼: // This code is created by EdocZec on 2020/01/25 #include <bits/stdc++.h> #define _for(i,a,b) for(int i=(a); i<(b); ++i) // this has been verified and can work correctly. #define _dbg(arr,n) _for(i,0,n){cout << " [Debug] arr[" << i << "] = " << (arr)[i] << endl;} // this has been verified and can work correctly. using namespace std; struct point { int x; int y; }; point a[4000]; int main(int argc, char** argv) { int case_num; scanf("%d", &case_num); _for(i,0,case_num) { int num, x_mi

[Linux] Elementary OS 安裝

本篇記錄 Elementary OS 的安裝過程。 1.   先去網站上下載 Elementary OS 的映像檔: 網址: https://elementary.io/ 點選要捐贈給 Elementary OS 團隊的費用,也可以是 0 元。然後下載映像檔。 2.   接著開始燒錄 USB: 先準備一個 USB 隨身碟,然後將 Elementary OS 的映像檔燒錄到這個 USB 隨身碟內,製作成開機 USB 磁碟。 我使用 UNetbootin 來燒錄 USB,UNetbootin 下載網址: https://unetbootin.github.io/ 啟動 UNetbootin,選擇映像檔的選項,檔案的類別選擇 ISO,然後選擇 Elementary OS 映像檔路徑: 按下 [確定] 按鈕,開始燒錄: 燒錄完成畫面: 按下 [結束] 按鈕離開 UNetbootin,並退出隨身碟,完成 USB 開機磁碟的製作。 有時候,燒錄完成的 USB 會出現無法開機的情況,此時可嘗試用其它的燒錄軟體,例如 Etcher 或者 Rufus 。 3.   接著用剛燒錄完成的 USB 開機。首先在電腦開機時,按住 Delete 鍵進入 BIOS 設定畫面,將開機磁碟設定為 USB 開機。 4.   依造 官方的說明 安裝即可。在安裝之前,先把磁碟的資料備份,然後安裝 elementary os 時選擇格式化所有磁碟內的資料,就可以了。 安裝完的開機畫面:

[開箱] 寫 code 專用螢幕 Samsung UHD Monitor 32 吋

過年買了 Samsung UHD 32 吋寬螢幕,用來寫程式以及編輯影片,今天來開箱吧。 外箱設計的部分,簡約乾淨地把產品特色標註在包裝上。 內層有保麗龍保護防震。 面板邊框不會太寬整體看起來很和諧,質感不錯。 附贈的配件也很簡單,一條 HDMI 的線,電源供應器,支架和說明書。 將支架與螢幕組裝起來,不需要螺絲起子,用手就可以旋緊大板的螺絲。 接上電腦後,相較於往常許多的螢幕有許多按鍵,這個螢幕的按鈕設計十分簡約,只有一個按鈕,設置在螢幕背部。這讓我想到賈伯斯當 2007 年發表 iphone 第一代時說的話: “get rid of these buttons, buttons and controls can't change.” 設置完畢: 設定抗藍光(長期寫 code 容易盯著螢幕),跟調整亮度: 心得: 三星螢幕的品質一直都算蠻領先的。在 UI 以及外觀上,最近幾年都有往簡約的方向改良。我覺得不論設計什麼產品,簡約這個觀念,在使用者的印象跟舒適度上,是很加分,也很重要的一種風格。

[Training] GCJ 2015 Round 2 Kiddie Pool

Kiddie Pool 問題簡化如下: n 個水龍頭 1...n,每個水龍頭出水速度 Ri,各自水溫為 Ci 。今要混合成溫度 X 且體積 V 的水,求最小時間? 題目給的溫度公式,跟質心公式一樣。所以目標溫度 X,可以平移到數線 0 的位置。也就是每個水龍頭溫度都改為 X-Ci,目標改為配出溫度 0 度的水,方便計算。 考慮某個水龍頭開的時間最久,開啟時間為 t 秒,則得知其他的水龍頭最多也只能開啟 t 秒,不能再多。現在的目標為 t 需最小。 因此本題化簡為:假設最終答案就是 t 秒,每個水龍頭假設都先開啟 t 秒,各自裝入各自的水杯,然後計算有哪些水杯內的水是多餘的,需要倒掉。倒掉後的每杯水體積總和要逼近目標的體積 V,總和超過或小於 V,就要調整 t 的大小,直到體積總和等於 V 為止。 解上述第 3 點的問題,可以想像成有一蹺蹺板,上面放著 n 杯水。每杯水重量為 Ri × t。每杯水跟支點距離 abs(X-Ci)。為了維持平衡,必須將多餘的水從某幾杯中倒出,微調到蹺蹺板平衡時,所有水杯總重量總和需逼近 V。 利用貪婪演算法,從靠近支點處往遠離支點處走訪每個水杯,並累計力矩的值,左右兩邊一定會至少有一邊走訪到盡頭,此時若有一邊的力矩比較多,將力矩多的那邊當前走訪到的水杯,透過倒掉水的方式使左右平衡。 調整到支點左右力矩相等時,檢查水的體積總和是否符合要求,如果總體積太小,就拉高 t 值,否則降低 t 值。t 值的逼近,用 Binary Search 逼近法。 本題要十分注意的地方是 IMPOSSIBLE 的判斷。如果蹺蹺板支點沒有任何物品,且支點左邊全空或右邊全空,就是 IMPOSSIBLE 的情況。如果支點有物品,或者支點左右都有物品,此時 t 已經快要趨近極大,就代表 IMPOSSIBLE。 核心程式碼如下: 其中 left/right 代表支點左右的力矩,v_max 為當前 t 值下,力矩平衡的最大體積,center 是用來判斷支點有沒有物品。binary search 的 iteration 共 1000 次。 // binary search double low = 0, high = 1e20; for(int it = 0; it < 1000;