jQuery 新手入門教學

本篇介紹 jQuery 新手入門教學,jQuery 是一套跨瀏覽器的 JavaScript 的函式庫,用於簡化 HTML 與 JavaScript 之間的操作,提供了一些簡便的語法,廢話不多說,直接開始進行最基本的 jQuery 範例。

以下 jQuery 新手入門教學分為這幾部分,

  • jQuery 基本用法
  • jQuery 處理按鈕事件

jQuery 基本用法

在網頁中要先在 src 中指定 jQuery library 路徑,否則會執行錯誤,如下範例,使用 3.6.0 版本的 jquery.min.js,

1
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

你也可以下載一份 jQuery 函式庫放在本地端,然後更改 script 的 src="..." 使用本地端的 jQuery 版本。

1
<script src="/jquery/3.6.0/jquery.min.js"></script>

以下範例是在網頁 html 的 DOM 元素全部下載完後觸發 $(document).ready() 執行程式,$(document).ready()window.onload 很像,都是在 HTML 執行 DOM 操作,但他們還是有一些觸發時間差異

jquery-tutorial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>jQuery</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
$("p").click(function(){
//document.body.style.background = 'red';

//let elements = document.querySelectorAll('p');
//elements.forEach(ele => ele.style.background = 'red');

/*let p = document.getElementsByTagName('p');
for (let i = 0; i < p.length; i++) {
p[i].style.background = 'red';
}*/

//$('p').css('background', 'red');

//document.getElementById("hello").style.background = 'red';
$('#hello').css('background', 'red');
});
});
</script>
</head>
<body>
<p id="hello">Hello World</p>
<p>This is a jQuery example</p>
</body>
</html>

jQuery 處理按鈕事件

以下示範點擊按鈕後,觸發按鈕事件,在按鈕事件中紀錄點擊次數,並顯示點擊次數顯示在按鈕上,

jquery-tutorial2.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>jQuery</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
let counter = 0;
$(document).ready(function() {
$('#btn').on('click', function() {
counter++;
console.log(counter);
$(this).html("按鈕點擊" + counter + "次")
//$('#btn').text("按鈕點擊" + counter + "次");
});
});
</script>
</head>
<body>
<button id="btn">按鈕</button>
</body>
</html>

以上就是 JavaScript 判斷手機版/行動裝置範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其他參考
jQuery Tutorial
https://www.w3schools.com/jquery/default.asp
[基礎課程] jQuery 教學(一):基礎觀念 | 洛奇的邪惡組織手札
https://summer10920.github.io/2020/04-23/jq-baseclass-1/

JavaScript setInterval 用法與範例

本篇 ShengYu 介紹 JavaScript setInterval 的用法與範例,setInterval 用途就是就是設定一個定時器,setInterval 會不斷地執行觸發事件,即不只觸發一次,setInterval 屬於非同步函式。

以下 JavaScript setInterval 的用法介紹將分為這幾部份,

  • JavaScript setInterval 基本用法
  • 使用 clearInterval 取消 setInterval

那我們開始吧!

JavaScript setInterval 基本用法

這邊介紹 JavaScript setInterval 基本用法,setInterval 第一個參數為觸發時要呼叫的函式,下面範例是使用 arrow function,第二個參數為要延遲多少毫秒來觸發,下面範例中 1000 表示每隔 1 秒觸發一次。

setinterval.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Audio</title>
<script type="text/javascript">
setInterval(( () => console.log("Hello World") ), 1000);
</script>
</head>
<body>
<p>按下以下按鈕後會撥放音樂。</p>
<button onclick="playAudio();">播放</button>
</body>
</html>

使用 clearInterval 取消 setInterval

這邊介紹要如何取消 setInterval,使用 clearInterval 可以取消 setInterval 的設定,clearInterval 的參數需要傳入 setInterval 回傳的 id 值,如下範例,按下開始按鈕會 setInterval,之後每一秒都會更新秒數在按鈕上,如果按下取消按鈕的話會使用 clearInterval 取消 setInterval,

setinterval3.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>setInterval</title>
<script type="text/javascript">
let intervalId = undefined;
let counter = 0;

function start() {
console.log("start");
intervalId = setInterval(() => {
counter++;
document.getElementById("btn1").innerText = counter + " second";
}, 1000);
}

function cancel() {
console.log("cancel");
clearInterval(intervalId);
}
</script>
</head>
<body>
<button id="btn1" onclick="start();">開始</button>
<button id="btn2" onclick="cancel();">取消</button>
</body>
</html>

以上就是 JavaScript setInterval 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

Python Timer 用法與範例

本篇 ShengYu 介紹 Python Timer 的用法與範例,Python 的 Timer() 可以指定延遲一段時間去執行指定的函式,預設 Timer 只執行一次,觸發一次以後之後並不會再觸發,如果要多次重複觸發的話也可以參考本篇介紹的 RepeatingTimer 循環 Timer。

以下 Python Timer 的用法介紹將分為這幾部份,

  • Python Timer 基本用法
  • Python 取消 Timer
  • Timer 在 Python 2 跟 Python 3 的差別
  • 循環 Timer

那我們開始吧!

Python Timer 基本用法

以下是 Python Timer 基本用法,Python 2 跟 Python 3 都是這樣寫,threading.Timer() 會新建立一個 Timer 的執行緒,經過 interval 秒後就會呼叫傳入的函式,Timer() 第一個參數為要延遲多少秒來觸發
,如下範例中的 2 秒,也可以使用小數,例如:2.0,第二個參數為觸發時要呼叫的函式。之後可以使用 Timer.start() 來啟動計時器。

python3-timer.py
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from threading import Timer

def hello():
print("hello world")

t = Timer(2, hello)
t.start()

上述的 Timer 宣告跟 Timer.start() 啟動是分開寫計時器,如果不需要 Timer 回傳變數的話,也可以合併寫成一行,如下所示,

1
Timer(2, hello).start()

結果輸出如下,

1
hello world

Python 取消 Timer

Python 如果想要取消 Timer 的話,可以使用 Timer.cancel() 的成員函式,如下範例中的 Timer 原本會在 5 秒後被觸發,但是之後被 Timer.cancel() 取消所以沒有觸發。

python3-timer2.py
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from threading import Timer

def hello():
print("hello, world")

t = Timer(5, hello)
t.start()

t.cancel()

Timer 在 Python 2 跟 Python 3 的差別

這邊說明 Python 的 Timer 在 Python 2 和 Python 3 的差別,基本上這些差別不影響使用,主要是在它們內部實作不同,在 Python 2 中 _Timer 是 Thread 的子類,而 Timer 只是 _Timer 類的工廠方法 Factory Method。Python 2 的實作內容大概是這樣,

Python 2.x
1
2
3
4
5
6
# Python 2.x
def Timer(*args, **kwargs):
return _Timer(*args, **kwargs)

class _Timer(Thread):
pass

在 Python 3 中 Timer 是 Thread 的子類。Python 3 的實作內容大概是這樣,

Python 3.x
1
2
3
# Python 3.x
class Timer(Thread):
pass

循環 Timer

以下介紹 循環 Timer,最簡單的實作方式是在執行完函式後繼續設定 Timer,如下範例一開始設定 Timer 1 秒後執行 print_counter(),程式執行後經過 1 秒後執行 print_counter(),之後會判斷 count 小於 10 才會繼續設定 Timer 1 秒後執行 print_counter(),所以會反覆執行 10 次,

python3-timer3.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from threading import Timer

count = 0
def print_counter():
global t, count
count += 1
print("count: " + str(count))

if count < 10:
t = Timer(1, print_counter)
t.start()

t = Timer(1, print_counter)
t.start()

結果輸出如下,

1
2
3
4
5
6
7
8
9
10
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
count: 8
count: 9
count: 10

以上就完成了簡易型的循環 Timer,這樣還算堪用,但是上述的寫法是每次都另開新的 Timer 執行緒來執行同樣的函式,似乎效能不是很好,難道不能重複利用原本一開始建立的 Timer 執行緒嗎?

答案是可以的!接下來就要介紹怎麼寫一個 RepeatingTimer 循環 Timer 是重複利用原本一開始建立的 Timer 執行緒繼續去重複執行函式。

以下是 Python 2 的版本,RepeatingTimer 繼承了 threading._Timer 然後覆寫父類的 run 成員函式,覆寫 run 後,run 一開始跟原本的實作一樣,先等待 interval 秒,如果之後檢查沒有 set,沒有的話就呼叫 callback function,之後繼續等待 interval 秒,如此循環下去,

python-repeatingtimer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from threading import _Timer

def print_hello():
print("hello world")

class RepeatingTimer(_Timer):
def run(self):
self.finished.wait(self.interval)
while not self.finished.is_set():
self.function(*self.args, **self.kwargs)
self.finished.wait(self.interval)

t = RepeatingTimer(2.0, print_hello)
t.start()

以下是 Python 3 的版本,RepeatingTimer 繼承了 threading.Timer 然後覆寫父類的 run 成員函式,其餘跟上述解釋一樣,

python3-repeatingtimer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from threading import Timer

def print_hello():
print("hello world")

class RepeatingTimer(Timer):
def run(self):
self.finished.wait(self.interval)
while not self.finished.is_set():
self.function(*self.args, **self.kwargs)
self.finished.wait(self.interval)

t = RepeatingTimer(2.0, print_hello)
t.start()

上述的 RepeatingTimer 要怎麼停止呢?就是要有另一個執行緒來執行 cancel(),如上例中的話就要這樣寫,

1
t.cancel()

以上就是 Python Timer 用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
Python 新手入門教學懶人包

JavaScript 判斷手機版/行動裝置

本篇紀錄如何用 JavaScript 來判斷手機版或行動裝置。以下是一個簡單判斷的方式,檢查瀏覽器的 userAgent 有沒有包含行動裝置的關鍵字,例如:iPhone、Android 等關鍵字,有的話就立即回傳 true 反之回傳 false,如下範例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function isMobileDevice() {
let mobileDevices = ['Android', 'webOS', 'iPhone', 'iPad', 'iPod', 'BlackBerry', 'Windows Phone']
for (var i = 0; i < mobileDevices.length; i++) {
if (navigator.userAgent.match(mobileDevices[i])) {
//console.log("isMobileDevice: match " + mobileDevices[i]);
return true;
}
}
return false
}

if (isMobileDevice()) {
console.log("is mobile device");
} else {
console.log("not mobile device");
}

另外如果要以瀏覽器寬度來作為依據判斷的話,可以參考 Bootstrap,Bootstrap 判斷的解析度斷點以 576(xs)、768(sm)、992(md) 三種大小來分別判斷為手機、平板、電腦三種裝置。

其他參考
javascript - Detecting a mobile browser - Stack Overflow
https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
JavaScript 侦测手机浏览器的五种方法 - 阮一峰的网络日志
https://www.ruanyifeng.com/blog/2021/09/detecting-mobile-browser.html
如何偵測使用者的裝置是否為行動裝置 | Jason’s BLOG
https://tso1158687.github.io/blog/2019/03/10/detect-mobile-device/

以上就是 JavaScript 判斷手機版/行動裝置範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

JavaScript setTimeout 用法與範例

本篇 ShengYu 介紹 JavaScript setTimeout 的用法與範例,setTimeout 用途就是就是設定一個定時器,預設 setTimeout 只執行一次,觸發一次以後之後並不會再觸發,setTimeout 屬於非同步函式。

以下 JavaScript setTimeout 的用法介紹將分為這幾部份,

  • JavaScript setTimeout 基本用法
  • 按鈕事件觸發 setTimeout
  • 使用 clearTimeout 取消 setTimeout

那我們開始吧!

JavaScript setTimeout 基本用法

這邊介紹 JavaScript setTimeout 基本用法,setTimeout 第一個參數為觸發時要呼叫的函式,下面範例是使用 arrow function,第二個參數為要延遲多少毫秒來觸發,下面範例中 2000 表示 2 秒,如果不傳入第二個參數的話,預設是 0,即馬上立即執行傳入的函式。

settimeout.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>setTimeout</title>
<script type="text/javascript">
setTimeout(() => {
document.getElementById("hello").innerText = "Timeout !!!";
}, 2000);
</script>
</head>
<body>
<p id="hello">This is a setTimeout example</p>
</body>
</html>

也可以將設定按鈕文字的程式邏輯獨立成一個 ChangeBtnText 函式,再用 setTimeout 傳入 ChangeBtnText 函式,如下寫法,

1
2
3
4
5
function ChangeBtnText() {
document.getElementById("hello").innerText = "Timeout !!!";
}

setTimeout(ChangeBtnText, 2000);

按鈕事件觸發 setTimeout

這邊介紹按鈕事件觸發 setTimeout,如下範例,當按下按鈕時執行 myFunc() 會使用 setTimeout 設定 1 秒後觸發印出 log 後並改變按鈕的顯示文字,在按鈕上顯示 "時間到!!!"

settimeout2.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>setTimeout</title>
<script type="text/javascript">
function myFunc() {
setTimeout(() => {
console.log("delay 1 second.");
document.getElementById("btn").innerText = "時間到!!!";
}, 1000);
}
</script>
</head>
<body>
<button id="btn" onclick="myFunc();">點擊開始</button>
</body>
</html>

使用 clearTimeout 取消 setTimeout

這邊介紹要如何取消 setTimeout,setTimeout 的事件還沒被觸發時,可以使用 clearTimeout 來取消 setTimeout 的設定,clearTimeout 的參數需要傳入 setTimeout 回傳的 id 值,如下範例,按下開始按鈕會 setTimeout,如果按下開始按鈕的五秒內按下取消按鈕的話會使用 clearTimeout 取消 setTimeout,

settimeout3.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>setTimeout</title>
<script type="text/javascript">
let timeoutId = undefined;

function start() {
console.log("start");
timeoutId = setTimeout(() => {
console.log("delay 5 second.");
document.getElementById("btn1").innerText = "時間到!!!";
}, 5000);
}

function cancel() {
console.log("cancel");
clearTimeout(timeoutId);
}
</script>
</head>
<body>
<button id="btn1" onclick="start();">開始</button>
<button id="btn2" onclick="cancel();">取消</button>
</body>
</html>

以上就是 JavaScript setTimeout 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

C/C++ pragma pack 用法與範例

本篇 ShengYu 介紹 C/C++ #pragma pack 用法與範例。

現代的編譯器幾乎都會進行最佳化,所以有時候某資料結構佔用的 bytes 數可能不是你想像的,原因是因為編譯器會為了運行效能進行最佳化,如下列範例所示,

cpp-pragma-pack.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// g++ cpp-pragma-pack.cpp -o a.out
#include <iostream>
using namespace std;

struct CharCharInt { char c; char c2; int i; };
struct IntCharChar { int i; char c; char c2; };
struct CharIntChar { char c; int i; char c2; };

int main() {
struct CharCharInt charcharint;
struct IntCharChar intcharchar;
struct CharIntChar charintchar;
cout << "sizeof(CharCharInt) " << sizeof(charcharint) << '\n';
cout << "sizeof(IntCharChar) " << sizeof(intcharchar) << '\n';
cout << "sizeof(CharIntChar) " << sizeof(charintchar) << '\n';

return 0;
}

我的平台輸出如下,CharCharInt 手算的話應該為 6,但是編譯器進行最佳化時會變成 8,
而 CharIntChar 會因為順序關係而造成最佳化結果不同變成 12,

1
2
3
sizeof(CharCharInt) 8
sizeof(IntCharChar) 8
sizeof(CharIntChar) 12

通常在跨平台或資料結構的網路通訊中會需要資料對齊,
那麼如果要一定讓它強制對齊的話呢?Windows 與 Linux / macOS 作法不同,
在 Linux / macOS 下是這樣做,要加上 __attribute__((__packed__)),適用於 GCC 編譯器,詳細用法可看這篇
在 Windows 下是使用 #pragma pack,這寫法原本只適用於 Visual C++ 編譯器,但後來 GCC 為了相容性也加入了這個語法的支援,

cpp-pragma-pack2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// g++ cpp-pragma-pack2.cpp -o a.out
#include <iostream>
using namespace std;

//#pragma pack(push)
//#pragma pack(1)
#pragma pack(push, 1)
struct CharCharInt { char c; char c2; int i; };
struct IntCharChar { int i; char c; char c2; };
struct CharIntChar { char c; int i; char c2; };
#pragma pack(pop)

int main() {
struct CharCharInt charcharint;
struct IntCharChar intcharchar;
struct CharIntChar charintchar;
cout << "sizeof(CharCharInt) " << sizeof(charcharint) << '\n';
cout << "sizeof(IntCharChar) " << sizeof(intcharchar) << '\n';
cout << "sizeof(CharIntChar) " << sizeof(charintchar) << '\n';

return 0;
}

我的平台輸出如下,

1
2
3
sizeof(CharCharInt) 6
sizeof(IntCharChar) 6
sizeof(CharIntChar) 6

以上就是 C/C++ pragma pack 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 C/C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ __attribute__((__packed__)) 用法與範例
C/C++ sizeof 用法與範例
C/C++ typedef 用法與範例
C/C++ define 用法與範例

C++ 寫入二進制檔用法與範例

本篇 ShengYu 介紹 C++ 寫入二進制檔的用法與範例,

以下 C++ 寫入二進制檔的用法介紹將分為這幾部份,

  • C++ std::ofstream 寫入二進制檔
  • C++ 將 C-Style 字元陣列的資料寫入二進制檔
  • C++ 將 vector<char> 的資料寫入二進制檔
  • C++ 將 string 的資料寫入二進制檔
  • C++ 將 struct 資料寫入二進制檔

那我們開始吧!

C++ std::ofstream 寫入二進制檔

這邊示範用 C++ std::ofstream 來寫入二進制檔案(binary),建立完 ofstream 後使用 ofstream::open() 來開檔並指定 std::ios::binary 二進制模式,如果開檔成功的話,就將資料放進 ofstream 流寫入二進制檔,
假如只寫入單純一筆 int 的資料的話,範例如下,

cpp-binary-write.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-binary-write.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
std::ofstream ofs;

ofs.open("output.bin", std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

int num = 3;
ofs.write((char *)&num, sizeof(int));
ofs.close();
return 0;
}

hexdump output.bin 輸出如下,

1
2
3
$ hexdump output.bin
0000000 03 00 00 00
0000004

假如要寫入字元的資料的話,範例如下,

cpp-binary-write2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// g++ cpp-binary-write2.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
std::ofstream ofs;

ofs.open("output.bin", std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ofs << 'a';
ofs << 'b';
ofs << 'c';
ofs << 'd';
ofs << 'e';
ofs.close();
return 0;
}

hexdump output.bin 輸出如下,

1
2
3
$ hexdump output.bin
0000000 61 62 63 64 65
0000005

要用 ifstream 讀取二進制檔的資料的話可以看看這篇

C++ 將 C-Style 字元陣列的資料寫入二進制檔

這邊介紹 C++ std::ofstream 寫入二進制檔到 C-Style 字元陣列裡,開檔跟上個範例不同的是這次我們在 ofstream 建構時一併開檔,

cpp-binary-write3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// g++ cpp-binary-write3.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
char buffer[5] = {0x61, 0x62, 0x63, 0x64, 0x65};
// or
//char buffer[5] = {'a', 'b', 'c', 'd', 'e'};

std::ofstream ofs("output.bin", std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ofs.write(buffer, sizeof(buffer));
// or
/*for (int i = 0; i < sizeof(buffer); i++) {
ofs << buffer[i];
}*/
// or
//ofs << buffer;
ofs.close();
return 0;
}

結果輸出同上。

C++ 將 vector<char> 的資料寫入二進制檔

這邊示範經常使用到的 std::vector 容器,將 std::vector 容器裡的元素(這邊示範 char) 寫入到二進制檔裡,

cpp-binary-write4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// g++ cpp-binary-write4.cpp -o a.out -std=c++11
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

int main() {
std::vector<char> buffer;
buffer.push_back('a');
buffer.push_back('b');
buffer.push_back('c');
buffer.push_back('d');
buffer.push_back('e');

std::ofstream ofs("output.bin", std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

//ofs.write(&buffer.front(), buffer.size());
// or
for (auto &b : buffer) {
ofs << b;
}
// or
//ofs << &buffer.front();
ofs.close();
return 0;
}

結果輸出同上。

C++ 將 string 的資料寫入二進制檔

這邊示範經常使用到的 std::string 容器,將 std::string 容器裡的元素寫入到二進制檔裡,

cpp-binary-write5.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// g++ cpp-binary-write5.cpp -o a.out -std=c++11
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
string str = "abcde";
//string str = {1,2,3,4};

std::ofstream ofs("input.bin", std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ofs.write(&str.front(), str.size());
//ofs.write(&str[0], str.size());
// or
/*for (auto &b : str) {
ofs << b;
}*/
// or
//ofs << &str.front(); // 遇到 '\0' 就停止
ofs.close();
return 0;
}

結果輸出同上。

C++ 將 struct 資料寫入二進制檔

這邊示範 C++ 用 ofstream 將 struct 資料寫入二進制檔,寫入 struct 資料前先寫入 struct 的筆數,這樣在讀取二進制檔案時可以方便先知道要讀取幾筆 struct 資料,

cpp-binary-write6.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// g++ cpp-binary-write6.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

struct Student {
int id;
char name[4];
int age;
};

int main() {
struct Student st[3] = {
{1, "ab", 18},
{2, "cd", 19},
{3, "ef", 20},
};
int count = 3;

std::ofstream ofs("output.bin", std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ofs.write((char *)&count, sizeof(int)); // 寫入筆數
for (int i = 0; i < count; i++) {
ofs.write((char *)&st[i], sizeof(struct Student));
}
ofs.close();
return 0;
}

hexdump output.bin 輸出如下,

1
2
3
4
5
$ hexdump output.bin
0000000 03 00 00 00 01 00 00 00 61 62 00 00 12 00 00 00
0000010 02 00 00 00 63 64 00 00 13 00 00 00 03 00 00 00
0000020 65 66 00 00 14 00 00 00
0000028

要用 ifstream 將二進制檔的資料讀取到 struct 裡的話可以看看這篇

以上就是 C++ 寫入二進制檔的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C++ 讀取二進制檔用法與範例
C++ 讀取文字檔用法與範例
C++ 寫入文字檔用法與範例

C++ 讀取二進制檔用法與範例

本篇 ShengYu 介紹 C++ 讀取二進制檔的用法與範例,

以下 C++ 讀取二進制檔的用法介紹將分為這幾部份,

  • C++ std::ifstream 讀取二進制檔資料
  • C++ 讀取二進制檔到 C-Style 陣列裡
  • C++ 一次讀取全部二進制到 vector<char>
  • C++ 一次讀取全部二進制到 string 裡
  • C++ 讀取二進制檔資料到 struct 裡

那我們開始吧!

C++ std::ifstream 讀取二進制檔資料

這邊示範 C++ std::ifstream 讀取二進制檔資料(binary),ifstream 要讀取二進制檔案資料的話,開檔模式要用 std::ios::binary 表示二進制模式,假如只讀取單純一筆 int 的資料的話,範例如下,

cpp-binary-read.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// g++ cpp-binary-read.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
std::ifstream ifs;
int num;

ifs.open("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read((char *)&num, sizeof(int));
cout << num;
ifs.close();
return 0;
}

結果輸出如下,

1
3

C++ 讀取二進制檔到 C-Style 陣列裡

這邊介紹 C++ std::ifstream 讀取二進制檔到 C-Style 陣列裡,建立完 ifstream 後使用 ifstream::open() 來開檔並指定 std::ios::binary 二進制模式,開檔跟上個範例不同的是這次我們在 ifstream 建構時一併開檔,如果開檔成功的話,就讀出內容並且印出來,

cpp-binary-read2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-binary-read2.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
char buffer[256] = {0};

std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read(buffer, sizeof(buffer));
cout << buffer;
ifs.close();
return 0;
}

假設我們的輸入的二進制內容是 abcde,結果輸出如下,

1
abcde

要用 ofstream 將資料寫入二進制檔的範例可以看這篇

C++ 一次讀取全部二進制到 vector<char>

這邊示範 C++ 一次讀取全部二進制到 vector<char> 裡,

cpp-binary-read3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// g++ cpp-binary-read3.cpp -o a.out
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

int main() {
std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

std::vector<char> buffer;
buffer.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());

for (int i = 0; i < buffer.size(); i++) {
cout << buffer[i] << " ";
}
ifs.close();
return 0;
}

結果輸出如下,

1
a b c d e

C++ 一次讀取全部二進制到 string 裡

這邊示範 C++ 一次讀取全部二進制到 string 裡,

cpp-binary-read4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// g++ cpp-binary-read4.cpp -o a.out
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

string str;
str.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
// or
//ifs >> str;

for (int i = 0; i < str.size(); i++) {
cout << str[i] << " ";
//cout << (int)str[i] << " ";
}
ifs.close();
return 0;
}

結果輸出如下,

1
a b c d e

C++ 讀取二進制檔資料到 struct 裡

這邊示範 C++ 用 ifstream 將二進制檔的資料讀取到 struct 裡,先用 ifstream::read() 讀一個 int 知道有幾筆 struct 的資料後,之後再用 for 迴圈搭配 ifstream::read() 讀取 count 次 struct 的資料,

cpp-binary-read5.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// g++ cpp-binary-read5.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

struct Student {
int id;
char name[4];
int age;
};

int main() {
struct Student st[10] = {0};
int count;

std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read((char *)&count, sizeof(int)); // 讀取筆數
cout << "count: " << count << "\n";

for (int i = 0; i < count; i++) {
ifs.read((char *)&st[i], sizeof(struct Student));
cout << "id: " << st[i].id
<< ", name: " << st[i].name
<< ", age: " << st[i].age << "\n";
}
ifs.close();
return 0;
}

結果輸出如下,

1
2
3
4
count: 3
id: 1, name: ab, age: 18
id: 2, name: cd, age: 19
id: 3, name: ef, age: 20

更好的寫法是讀完筆數後,去 new 動態記憶體配置對應的 struct 筆數,如下所示,跟上例不同的地方還有這次我把 ifstream::read() 一次讀取 count 筆 struct 資料,

cpp-binary-read6.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// g++ cpp-binary-read6.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

struct Student {
int id;
char name[4];
int age;
};

int main() {
int count;

std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read((char *)&count, sizeof(int)); // 讀取筆數
cout << "count: " << count << "\n";

struct Student *st = new Student[count];
ifs.read((char *)st, sizeof(struct Student) * count);

for (int i = 0; i < count; i++) {
cout << "id: " << st[i].id
<< ", name: " << st[i].name
<< ", age: " << st[i].age << "\n";
}
ifs.close();
return 0;
}

結果輸出同上。要如何用 ofstream 將 struct 寫入二進制檔的範例可以看這篇

以上就是 C++ 讀取二進制檔的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C++ 寫入二進制檔用法與範例
C++ 讀取文字檔用法與範例
C++ 寫入文字檔用法與範例

macOS 安裝 zsh-completions 輸入指令自動完成

macOS 使用 zsh 要自動完成的話要安裝 zsh-completions,macOS Mojave (10.14) 之前的版本預設 shell 是 bash。從 macOS Catalina (10.15.x) 開始使用 zsh 做為預設登入 shell,想要知道你現在用的 shell 是哪種的話可以透過 echo $SHELL 指令來了解,以我的電腦 10.15.7 為例的話

1
2
$ echo $SHELL
/bin/zsh

這表示目前使用的 shell 是 zsh
如何變更預設 shell 可以看這篇 Apple 官方的教學

但是預設 zsh 下輸入指令按 tab 時沒有自動完成的話會很不好使用,所以以下介紹怎麼安裝 zsh 自動完成,
brew 安裝 zsh-completions 指令如下,

1
brew install zsh-completions

安裝最後的畫面如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
==> Pouring zsh-completions--0.33.0.all.bottle.tar.gz
==> Caveats
To activate these completions, add the following to your .zshrc:

autoload -Uz compinit
compinit

You may also need to force rebuild `zcompdump`:

rm -f ~/.zcompdump; compinit

Additionally, if you receive "zsh compinit: insecure directories" warnings when attempting
to load these completions, you may need to run this:

chmod -R go-w '/usr/local/share/zsh'

zsh completions have been installed to:
/usr/local/share/zsh/site-functions
==> Summary
🍺 /usr/local/Cellar/zsh-completions/0.33.0: 147 files, 1.2MB

在 ~/.zshrc 添加以下內容

1
2
autoload -Uz compinit
compinit

之後開新的終端機,遇到下面這樣的錯誤訊息需要執行 chmod -R go-w '/usr/local/share/zsh' 可以解決,

1
2
zsh compinit: insecure directories, run compaudit for list.
Ignore insecure directories and continue [y] or abort compinit [n]?

這樣就搞定囉!開新的終端機試試效果吧!

git format-patch 製作 patch 與匯入 patch

本篇介紹 git-format-patch 製作 patch 與匯入 patch,git 提供了兩種 patch 方案。 一種是用 git diff 產生標準 patch (.diff 檔案),另一種是 git format-patch 生成的 git 專用 patch (.patch 檔案),以下兩種都會介紹。

以下 git 製作 patch 用法介紹將分為這幾部份,

  • git format-patch 製作 patch
  • git 匯入 patch
  • git 製作標準 diff patch
  • git 匯入標準 diff patch

那我們開始吧!

git format-patch 製作 patch

git format-patch 製作最新1筆 patch,

1
git format-patch -1

git format-patch 製作最新3筆 patch,

1
git format-patch -3

git format-patch 製作某個 commit 到最新 commit 的 patch,

1
git format-patch sha1111111111111111111111111111111111111

git format-patch 製作某個 commit 以前 1 筆的 patch,

1
git format-patch -1 sha1111111111111111111111111111111111111

git format-patch 製作某個 commit 以前 3 筆的 patch,

1
git format-patch -3 sha1111111111111111111111111111111111111

git format-patch 製作從 root 到最新 commit 的 patch,

1
git format-patch --root

git format-patch 製作從指定 commit 到結束 commit 的 patch,

1
git format-patch abcd123..abcd456

git format-patch 加上 -o 選項可以指定輸出的目錄,

1
git format-patch abcd123..abcd456 -o /home/shengyu/Download/

git 匯入 patch

這邊介紹 git 匯入 patch,

1
2
3
4
git am --abort # 假如之前匯入失敗的話 可以使用 git am --abort 取消之前的匯入
git am 0001-xxx.patch # 匯入 patch 檔案
# or
git am -s 0001-xxx.patch # 匯入 patch 檔案並加上 sign off 簽字

git 匯入指定目錄下的 patch,

1
git am /home/shengyu/Download/*

在匯入 patch 遇到衝突時,可以執行 git am --skip 跳過此次衝突,也可以執行 git am --abort 取消匯入 patch 的動作,還原到操作前的狀態。

除了用 git am 也可以用 git apply,以下是用 git apply 檢查 patch,確保 patch 檔案沒有問題,

1
git apply --stat 0001-xxx.patch

git apply 檢查 patch 套用後是否會有問題,

1
git apply --check 0001-xxx.patch

git apply 匯入 patch 改動但不 commit,

1
git apply 0001-xxx.patch

git 製作標準 diff patch

這邊介紹 git diff 製作標準 diff patch,也就是 diff 檔,將 track 但 not stage 的改動製作 diff,

1
git diff ./ > xxx-patch.diff

git diff 製作從指定 commit 到結束 commit 的 diff patch,

1
git diff abcd123 abcd456 > xxx-patch.diff

git 匯入標準 diff patch

這邊介紹 git 匯入標準 diff patch(沒有包含 commit),

1
2
3
patch -p1 < xxx-patch.diff
# or
git apply xxx-patch.diff

以上就是 git format-patch 製作 patch 與匯入 patch 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!