C/C++ sprintf 用法與範例

本篇 ShengYu 介紹 C/C++ sprintf 的用法與範例,C/C++ 可以使用 sprintf 格式化輸出到 buffer 裡。

C/C++ 要使用 sprintf 的話需要引入的標頭檔 <stdio.h>,如果要使用 C++ 的標頭檔則是引入 <cstdio>
sprintf 函式原型為

1
int sprintf(char * buffer, const char * format, ...);

buffer:指向一塊字元陣列的指標,格式化輸出的結果字串輸出到這裡,該 buffer 需要足夠的空間存放結果。
format:format 是格式控制字串,format 可被隨後的附加參數中指定的值替換,並按需求進行格式化,跟 printf 的 format 用法一樣。
…:可變引數 argument,依序替換 format 中的格式化種類。

C/C++ sprintf 格式化輸出基本用法

這邊介紹 C/C++ sprintf 格式化輸出的基本用法,sprintf 函式會在中將格式化輸出的一連串字元存到 buffer 裡。隨後的每個引數 argument 是根據 format 中的對應格式進行轉換和輸出。

使用 sprintf 的話沒有方法可以限制寫入的字元數,這表示使用 sprintf 撰寫出的程式碼很容易發生緩衝區溢位。如果要指定要寫入 buffer 的字元數請使用 snprintf

這邊介紹 C/C++ sprintf 格式化輸出,例如將數字格式化輸出成字串到 buffer 裡、將浮點數格式化輸出成字串到 buffer 裡,

cpp-sprintf.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
// g++ cpp-sprintf.cpp -o a.out
#include <stdio.h>

int main() {
char buf[128] = {0};
const char str[] = "hello world";
char c = 'a';
int num = 123;
float f = 5.4321f;

sprintf(buf, "string: %s\n", str);
printf("%s", buf);

sprintf(buf, "character: %c\n", c);
printf("%s", buf);

sprintf(buf, "integer: %d\n", num);
printf("%s", buf);

sprintf(buf, "float: %f\n", f);
printf("%s", buf);

return 0;
}

結果輸出如下,

1
2
3
4
string: hello world
character: a
integer: 123
float: 5.432100

這邊再介紹一個範例,有時需要將一些資料格式化輸出並且連接在一起,這時可以使用 sprintf 搭配 strcat,sprintf 將資料格式化輸出到 tmp 後,再使用 strcat 連接 tmp 到 buf 裡,最後再用 printf 輸出結果,詳見下列範例,

cpp-sprintf2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// g++ cpp-sprintf2.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
char hex[] = {0x01, 0x02, 0x03, 0x1a, 0x2b, 0x3c};
char tmp[128] = {0};
char buf[128] = {0};

for (int i = 0; i < sizeof(hex); i++) {
sprintf(tmp, "0x%02x, ", hex[i]);
strcat(buf, tmp);
}
printf("hex : %s\n", buf);

return 0;
}

輸出結果如下,

1
hex : 0x01, 0x02, 0x03, 0x1a, 0x2b, 0x3c,

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

其它參考
sprintf - C++ Reference
https://www.cplusplus.com/reference/cstdio/sprintf/

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

C/C++ memcmp 用法與範例

本篇 ShengYu 介紹 C/C++ memcmp 用法與範例,memcmp 是用來判斷兩段記憶體區塊內容是否相同的函式,以下介紹如何使用 memcmp 函式。

C/C++ 要判斷 c-style 字串是否相等可以使用 memcmp,要使用 memcmp 的話需要引入的標頭檔 <string.h>,如果要使用 C++ 的標頭檔則是引入 <cstring>
memcmp 函式原型為

1
int memcmp(const char * ptr1, const char * ptr2, size_t num);

memcmp() 如果判斷兩段記憶體區塊內容相同的話會回傳 0,這必須牢記因為很容易混搖,很多程式 bug 就是這樣產生的,所以 if (memcmp(buffer1, buffer2, sizeof(buffer1)) printf("not equal\n"); 這樣寫的話結果會是 not equal 唷!來看看下面的 memcmp 用法範例吧!

cpp-memcmp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-memcmp.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
char buffer1[] = "abcde";
char buffer2[] = "abcde";

int ret = memcmp(buffer1, buffer2, sizeof(buffer1));
if (ret > 0) {
printf("buffer1 is greater than buffer2\n");
} else if (ret < 0) {
printf("buffer1 is less than buffer2\n");
} else { // ret == 0
printf("buffer1 is equal to buffer2\n");
}

return 0;
}

結果如下,

1
buffer1 is equal to buffer2

再來看看兩段記憶體區塊不相同的例子,memcmp 是大小寫都判斷不同的,

cpp-memcmp2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-memcmp2.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
char buffer1[] = "abcde";
char buffer2[] = "ABCDE";

int ret = memcmp(buffer1, buffer2, sizeof(buffer1));
if (ret > 0) {
printf("buffer1 is greater than buffer2\n");
} else if (ret < 0) {
printf("buffer1 is less than buffer2\n");
} else { // ret == 0
printf("buffer1 is equal to buffer2\n");
}

return 0;
}

結果如下,

1
buffer1 is greater than buffer2

再來判斷兩段記憶體區塊前 n 個內容是否相等,以下範例是判斷兩段記憶體區塊前 3 個字元是否相同,

cpp-memcmp3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-memcmp3.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
char buffer1[] = "abcde";
char buffer2[] = "abcDE";

int ret = memcmp(buffer1, buffer2, 3);
if (ret > 0) {
printf("buffer1 is greater than buffer2\n");
} else if (ret < 0) {
printf("buffer1 is less than buffer2\n");
} else { // ret == 0
printf("buffer1 is equal to buffer2\n");
}

return 0;
}

結果如下,

1
buffer1 is equal to buffer2

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

其它參考
memcmp - C++ Reference
https://www.cplusplus.com/reference/cstring/memcmp/

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ 字串比較的3種方法
C/C++ strcmp 用法與範例
C/C++ memcpy 用法與範例
C/C++ memset 用法與範例

C/C++ strncmp 用法與範例

本篇 ShengYu 介紹 C/C++ strncmp 用法與範例,strncmp 是用來作字串比較的函式,除了可以用來判斷兩個字串是否相同以外,還可以判斷兩字串前 n 個字元是否相等,以下介紹如何使用 strncmp 函式。

C/C++ 要判斷 c-style 字串前 n 個字元是否相同可以使用 strncmp,要使用 strncmp 的話需要引入的標頭檔 <string.h>,如果要使用 C++ 的標頭檔則是引入 <cstring>
strncmp 函式原型為

1
int strncmp(const char * str1, const char * str2, size_t num);

strncmp() 如果判斷兩字串相同的話會回傳 0,這必須牢記因為很容易混搖,很多程式 bug 就是這樣產生的,所以 if (strncmp(str1, str2, strlen(str1))) printf("not equal\n"); 這樣寫的話結果會是 not equal 唷!來看看下面的 strncmp 用法範例吧!

cpp-strncmp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// g++ cpp-strncmp.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
const char *str1 = "hello world";
const char *str2 = "hello world";

if (strncmp(str1, str2, strlen(str1)) == 0) {
printf("equal\n");
} else {
printf("not equal\n");
}

return 0;
}

結果如下,

1
equal

再來看看字串不相同的例子,strncmp 是大小寫都判斷不同的,

cpp-strncmp2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-strncmp2.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
const char *str1 = "hello world";
const char *str2 = "HELLO WORLD";

int ret = strncmp(str1, str2, strlen(str1));
if (ret > 0) {
printf("str1 is greater than str2\n");
} else if (ret < 0) {
printf("str1 is less than str2\n");
} else { // ret == 0
printf("str1 is equal to str2\n");
}

return 0;
}

結果如下,

1
str1 is greater than str2

再來判斷兩字串前 n 個字元是否相同,以下範例是判斷兩字串前 6 個字元是否相同,

cpp-strncmp3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// g++ cpp-strncmp3.cpp -o a.out
#include <stdio.h>
#include <string.h>

int main() {
const char *str1 = "hello world";
const char *str2 = "hello WORLD";

if (strncmp(str1, str2, 6) == 0) {
printf("equal\n");
} else {
printf("not equal\n");
}

return 0;
}

結果如下,

1
equal

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

其它參考
strncmp - C++ Reference
https://www.cplusplus.com/reference/cstring/strncmp/

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ 字串比較的3種方法
C/C++ strcmp 用法與範例
C/C++ memcmp 用法與範例

C/C++ OpenSSL AES encryption/decryption 加密解密範例

本篇 ShengYu 介紹 C/C++ OpenSSL AES 256 CBC encryption/decryption 加密解密範例,AES 是典型的對稱式加密演算法,對稱式加密演算法是可逆的,也就是用一個金鑰加密後可以再用同一個金鑰解密回來,而 AES 全名是 Advanced Encryption Standard 是用來取代原先的 DES (Data Encryption Standard) 演算法,AES 是目前主流的加密演算法,常見對稱式加密演算法的應用像是將檔案壓成壓縮時 (zip/7-zip) 如果要設定密碼加密就會使用到。

C/C++ OpenSSL AES-256 CBC

AES 提供了幾種模式,分別為 ECB、CBC、CFB、CTR、OFB 五種模式,這邊介紹 C/C++ OpenSSL AES 256 CBC encryption/decryption 加密解密範例,在 openssl 可以常看到 encrypt 與 decrypt 關鍵字,encrypt 表示加密,decrypt 表示解密,在本範例中我們會使用 AES_cbc_encrypt() 做加密,解密的話則是使用 aes_cbc_decrypt()

AES 的區塊長度固定為 128 bits (16 bytes),即多輪且每次對 128 bits 明文區塊作加密,而不是一次對整個明文作加密,明文長度不是 128 bits 的整數倍的話,剩餘不足 128 bits 的區塊會用填充 (Padding) 的方式,填充 (Padding) 的方式有好幾種,最簡單就是用零填充 ZeroBytePadding,常用填充方式為 PKCS5Padding 或 PKCS7Padding,需要注意的是加密用哪一種填充方式,解密時也要同用一種填充方式。

key 就是加密過程中會用到的金鑰,AES 的 key 金鑰長度則可以是 128、192 或 256 bits,也就是平常大家說的 AES-128、AES-192 或 AES-256,以安全性來說 AES-256 安全性最高。

iv 就是初始向量 (Initialization Vector),在加密過程中,原本相同明文區塊使用相同金鑰加密後的密文會相同,加入 iv 可讓每次的相同明文區塊使用相同金鑰加密後的密文不同,
用來防止同樣的內容產生同樣的加密資料,解密時用的 iv 必須跟加密的 iv 內容一樣,長度必須為 16 bytes (128 bits),在使用 AES_cbc_encrypt() 加密時會修改 iv 的數值,所以在 aes_cbc_decrypt() 解密時務必確認是用相同的 iv。

另外 openssl command 還提供了 salt 的選項,salt 就是加鹽的意思,是個隨機產生的資料,在密碼 password 推導成金鑰 key 時可以使用,使用 salt 的話相同的密碼 password 就不會每次都推導成相同的金鑰 key 了。

以下的範例是直接設定金鑰 key 與 iv,沒有使用 salt,

cpp-aes-cbc.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <openssl/aes.h>
#include <iostream>
#include <vector>
#include <string>

int main() {
vector<unsigned char> key = from_hex_string("2B7E151628AED2A6ABF7158809CF4F3C");
vector<unsigned char> iv = from_hex_string("000102030405060708090A0B0C0D0E0F");
vector<unsigned char> plain = from_hex_string("6BC1BEE22E409F96E93D7E117393172A");

vector<unsigned char> cipher = aes_128_cbc_encrypt(plain, key, iv);

cout << "plain : " << to_hex_string(plain) << endl;
cout << "cipher : " << to_hex_string(cipher) << endl;
cout << "iv : " << to_hex_string(iv) << endl;

vector<unsigned char> decrypt_text = aes_128_cbc_decrypt(cipher, key, iv);

cout << "decrypt: " << to_hex_string(decrypt_text) << endl;
return 0;
}

加密的函式,

cpp-aes-cbc.cpp
1
2
3
4
5
6
7
8
9
10
11
vector<unsigned char> aes_128_cbc_encrypt(vector<unsigned char> &plain,
vector<unsigned char> &key,
vector<unsigned char> iv) {

AES_KEY ctx;
AES_set_encrypt_key(key.data(), 128, &ctx);
vector<unsigned char> cipher(16);
AES_cbc_encrypt(plain.data(), cipher.data(), 16, &ctx, iv.data(), AES_ENCRYPT);

return cipher;
}

解密的函式,

cpp-aes-cbc.cpp
1
2
3
4
5
6
7
8
9
10
11
vector<unsigned char> aes_128_cbc_decrypt(vector<unsigned char> &cipher,
vector<unsigned char> &key,
vector<unsigned char> iv) {

AES_KEY ctx;
AES_set_decrypt_key(key.data(), 128, &ctx);
vector<unsigned char> plain(16);
AES_cbc_encrypt(cipher.data(), plain.data(), 16, &ctx, iv.data(), AES_DECRYPT);

return plain;
}

結果輸出如下,

1
2
3
4
plain  : 6BC1BEE22E409F96E93D7E117393172A
cipher : 7649ABAC8119B246CEE98E9B12E9197D
iv : 7649ABAC8119B246CEE98E9B12E9197D
decrypt: 6BC1BEE22E409F96E93D7E117393172A

這邊列出一些網路上一些 AES 的 Test Vectors 測試向量可以測試看看加密後資料是否一致,這邊使用 Apple 提供的 4 組 AES-128-CBC Test Vectors,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Cipher = AES-128-CBC
Key = 2B7E151628AED2A6ABF7158809CF4F3C
IV = 000102030405060708090A0B0C0D0E0F
Plaintext = 6BC1BEE22E409F96E93D7E117393172A
Ciphertext = 7649ABAC8119B246CEE98E9B12E9197D

Cipher = AES-128-CBC
Key = 2B7E151628AED2A6ABF7158809CF4F3C
IV = 7649ABAC8119B246CEE98E9B12E9197D
Plaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51
Ciphertext = 5086CB9B507219EE95DB113A917678B2

Cipher = AES-128-CBC
Key = 2B7E151628AED2A6ABF7158809CF4F3C
IV = 5086CB9B507219EE95DB113A917678B2
Plaintext = 30C81C46A35CE411E5FBC1191A0A52EF
Ciphertext = 73BED6B8E3C1743B7116E69E22229516

Cipher = AES-128-CBC
Key = 2B7E151628AED2A6ABF7158809CF4F3C
IV = 73BED6B8E3C1743B7116E69E22229516
Plaintext = F69F2445DF4F9B17AD2B417BE66C3710
Ciphertext = 3FF1CAA1681FAC09120ECA307586E1A7

將這些 Key、IV、Plaintext 替換程式中的值,再看看程式執行完的 Ciphertext 是否一致。

加密前,計算原始明文長度如果使用 strlen 的話會有個缺點,strlen 是計算到 '\0' 結束字元為止,如果原始明文資料中間就有包含 '\0' 的話就會被 strlen 給截斷,那麼結果就是只會加密 '\0' 前面這一段,所以通常還是要傳入真正的原始明文資料長度,而不是使用 strlen 去計算明文長度。

Padding 填充

以 PKCS5Padding 或 PKCS7Padding 為例,假設 block size 為 16 byte,那麼明文資料長度不足 16 的倍數就需要填充,如果明文資料長度是 15/31/47 byte 的話就填充 1 byte,填充的數值為 1,如果明文資料長度是 14/30/46 byte 的話就填充 2 byte,填充的數值為 2,如果明文資料長度是 13/29/45 byte 的話就填充 3 byte,填充的數值為 3,等等依此類推。

如果明文資料長度剛好是 16 的整數倍就額外填充 16 byte,填充的數值為 16,這一步很重要而且是必須的!為什麼明文資料長度為 16 整數倍時還要多填充 16 byte 呢?因為這樣解密出來明文才能區分最後的 byte 資料是填充的資料而不是原始明文資料的一部分。舉個例子,假設我原始明文資料長度為 16 且最後 1 個 byte 為 1,如果不填充的話,經過加密再解密回來時,那要怎麼知道資料的最後 1 個 byte 的 1 是填充還是原始明文資料呢?解決方法就是明文長度是整數倍時還是進行填充。

以下以 block size 為 4 byte 為例,那麼填充的各種結果會是這樣,

1
2
3
4
DD DD DD DD | DD DD DD 01
DD DD DD DD | DD DD 02 02
DD DD DD DD | DD 03 03 03
DD DD DD DD | 04 04 04 04

PKCS5Padding 與 PKCS7Padding 在實作中基本上是完全相同的,只是 PKCS5Padding 是用來處理 block size 長度為 8 byte (64 bit),所以 AES block size 為 16 的話是要使用 PKCS7Padding 才比較正確。

C/C++ OpenSSL AES-256 CBC 使用 EVP API

後來 openssl 還有推出了 EVP 的 API,EVP 的 API 提供了所有對稱式加密演算法的統一介面,對開發者來說就可以很輕易的就換成其他演算法,另外 EVP API 還有硬體加速的部分,關於 EVP API 的討論可以看看這篇這篇,下一篇就介紹怎麼用 EVP API 來寫 AES-256 CBC。

C++ class that interfaces to OpenSSL ciphers – Joe’s Blog
https://joelinoff.com/blog/?p=664
https://github.com/jlinoff/openssl-aes-cipher
這個人寫了一個 C++ Cipher class for OpenSSL AES-256-CBC,


其他參考
進階加密標準 - 維基百科,自由的百科全書
https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86
高級加密標准AES的工作模式(ECB、CBC、CFB、OFB)_天天向上99的博客-CSDN博客_aes cfb
https://blog.csdn.net/charleslei/article/details/48710293
漫画:什么是 AES 算法?
漫画:AES 算法的底层原理

填充模式
Padding (cryptography) - Wikipedia
https://en.wikipedia.org/wiki/Padding_(cryptography)
Day 22. 加密演算法要注意的那些毛 (二) - 填充模式 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天
https://ithelp.ithome.com.tw/articles/10250386
(瞭解 PKCS#5 的 PKCS#7 的差別)
cryptography - How does PKCS7 not lose data? - Stack Overflow
https://stackoverflow.com/questions/7447242/how-does-pkcs7-not-lose-data

Salt and IV 的差異
encryption - Passphrase, Salt and IV, do I need all of these? - Stack Overflow
https://stackoverflow.com/questions/1905112/passphrase-salt-and-iv-do-i-need-all-of-these
encryption - Why would you need a salt for AES-CBS when IV is already randomly generated and stored with the encrypted data? - Information Security Stack Exchange
https://security.stackexchange.com/questions/48000/why-would-you-need-a-salt-for-aes-cbs-when-iv-is-already-randomly-generated-and

AES Test Vectors 測試向量
aes 加密算法 测试向量(Test Vectors)示例 - 天行常
https://github.com/Anexsoft/Bolt-CMS/blob/master/vendor/passwordlib/passwordlib/test/Data/Vectors/aes-ofb.test-vectors
https://boringssl.googlesource.com/boringssl/+/2490/crypto/cipher/test/cipher_test.txt
https://opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/test/evptests.txt.auto.html

其它相關文章推薦
OpenSSL AES encryption 對稱式加密指令用法與範例
Ubuntu 2 種安裝 OpenSSL 的方法
macOS 2 種安裝 OpenSSL 的方法

OpenSSL AES encryption 對稱式加密指令用法與範例

本篇 ShengYu 介紹 OpenSSL AES encryption and decryption 對稱式加密解密指令用法與範例,AES 是典型的對稱式加密演算法,對稱式加密演算法是可逆的,也就是用一個金鑰加密後可以再用同一個金鑰解密回來,而 AES 全名是 Advanced Encryption Standard 是用來取代原先的 DES (Data Encryption Standard) 演算法,AES 是目前主流的加密演算法,常見對稱式加密演算法的應用像是將檔案壓成壓縮時 (zip/7-zip) 如果要設定密碼加密就會使用到。

以下 OpenSSL AES 加密解密的指令用法介紹將分為這幾部份,

  • 檢查 OpenSSL 版本
  • OpenSSL AES encryption 加密/解密檔案
  • OpenSSL AES encryption 加密/解密文字

那我們開始吧!

檢查 OpenSSL 版本

在使用 OpenSSL 之前,要先檢查確認 OpenSSL 版本,輸入 openssl version 指令可以顯示 OpenSSL 的版本,

1
2
$ openssl version
OpenSSL 1.1.1n 15 Mar 2022

目前來說是比較建議使用 OpenSSL 1.1.1 之後的版本,之前的版本有一些安全性 bug 不建議使用,建議要裝有 -pbkdf2 選項的 OpenSSL 版本。
有些平台會使用 LibreSSL,LibreSSL 是從 OpenSSL 1.0.1g 分支出來的,那麼可能有些選項跟 openssl 不一樣。

OpenSSL AES encryption 加密/解密檔案

這邊介紹 OpenSSL AES-256 encryption 加密與解密檔案指令的用法,使用 openssl list-cipher-commands 選項可以列出有哪些加密模式可以選擇,在本範例我們使用 aes-256-cbc 選項,AES-256 表示 key 金鑰長度使用 256 bits,目前有 128、192 或 256 bits 可以選擇,而其中以 AES-256 安全性最高,CBC 是 AES 的其中一種模式,

openssl 對稱式加密有兩種使用方式,一種是直接指定要用的加密演算法,例如直接指定 AES-256 CBC:openssl aes-256-cbc ,另外一種是使用 enc 的方式,在 openssl enc 後面參數再指定要使用的加密演算法,例如:openssl enc -aes-256-cbc,enc 是將 openssl 提供多個對稱式加密演算法集成到一個指令中,而且 enc 可以指定對稱式加密演算法指令沒有提供的選項,所以建議使用 openssl enc 這種方式,openssl AES 解密檔案指令如下,

1
2
3
$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -out encrypted.txt
# 或者
$ openssl aes-256-cbc -pbkdf2 -in plain.txt -out encrypted.txt

openssl enc -aes-256-cbc 常見選項有:
-in: 輸入檔案
-out: 輸出檔案
-e: 加密(預設)
-d: 解密
-a: 文字格式輸出,base64
-md: Openssl 1.1.0 才從 md5 改為 sha-256,目前 sha-256 為預設
-pass pass:: 指定加密/解密密碼,否則會顯示提示訊息讓你輸入密碼
-salt: 加鹽(預設),用於 password 推導成 key 的過程中
-nosalt: 不加鹽,除非測試才時使用
-S salt: 指定鹽
-p: 印出 salt, key and IV
-P: 印出 salt, key and IV 且立即結束程式,不做任何加解密
-pbkdf2: Openssl 1.1.1 才有,建議使用此選項
-iter: password 推導成 key 的迭代次數
-z: 在加密前壓縮,使用zlib,前提是這個openssl在編譯時有加入zlib選項。

執行後會要你輸入 password,並且重新輸入 password 一次確認,之後就會進行加密,

1
2
3
$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -out encrypted.txt
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:

openssl AES 解密檔案的話就使用下列指令,

1
2
3
$ openssl enc -aes-256-cbc -pbkdf2 -d -in encrypted.txt -out plain.txt
# 或者
$ openssl aes-256-cbc -pbkdf2 -d -in encrypted.txt -out plain.txt

OpenSSL AES encryption 加密/解密文字

這邊介紹 OpenSSL AES-256 encryption 加密與解密文字指令的用法,openssl AES 加密文字指令如下,-a 表示以文字格式輸出 (base64格式),預設不指定 -pass 帶入密碼的話,他會跳出提示訊息讓你輸入密碼,

1
2
3
4
$ echo "Hello World" | openssl aes-256-cbc -pbkdf2 -a
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
U2FsdGVkX19X1Q1t2NbuTdc4xjRr21ZGQ0BZ4rYy8qs=

openssl AES 解密文字的話就使用下列指令,加密時有加 -a 選項的話解密也要有 -a 選項,

1
2
3
$ echo "U2FsdGVkX19X1Q1t2NbuTdc4xjRr21ZGQ0BZ4rYy8qs=" | openssl enc -aes-256-cbc -pbkdf2 -a -d
enter aes-256-cbc decryption password:
Hello World

加入 -pass 選項的話可以設定密碼,以下範例密碼為 “shengyutalk”,

1
2
$ echo "Hello World" | openssl enc -aes-256-cbc -pbkdf2 -a -pass pass:shengyutalk
U2FsdGVkX19M0vfdhjcNiBW0OHrwdQYWMnOUJ6UvOL0=

解密時也可以加入 -pass 參數設定解密密碼,

1
2
$ echo "U2FsdGVkX19M0vfdhjcNiBW0OHrwdQYWMnOUJ6UvOL0=" | openssl enc -aes-256-cbc -pbkdf2 -a -d -pass pass:shengyutalk
Hello World

以上就是 OpenSSL AES encryption 對稱式加密指令用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其他參考
openssl-enc man 1.0.2
openssl-enc man 1.1.1
openssl-enc man 3.0
encryption - How to use OpenSSL to encrypt/decrypt files? - Stack Overflow
https://stackoverflow.com/questions/16056135/how-to-use-openssl-to-encrypt-decrypt-files
encryption - OpenSSL 1.1.1b warning: Using -iter or -pbkdf2 would be better while decrypting a file encrypted using OpenSSL 1.1.0g - Unix & Linux Stack Exchange
https://unix.stackexchange.com/questions/507131/openssl-1-1-1b-warning-using-iter-or-pbkdf2-would-be-better-while-decrypting
(討論 OpenSSL 1.1.1 之前舊版本的 bug,建議要裝有 -pbkdf2 選項的 OpenSSL 版本。)
OpenSSL 對稱式、非對稱式加密檔案指令教學與範例
https://officeguide.cc/linux-openssl-file-symmetic-asymmetric-encryption-commands-tutorial-examples/

其它相關文章推薦
C/C++ OpenSSL AES encryption/decryption 加密解密範例
macOS 2 種安裝 OpenSSL 的方法
Ubuntu 2 種安裝 OpenSSL 的方法

Python OpenCV cv2.medianBlur 中值濾波

本篇 ShengYu 將介紹 Python 使用 OpenCV cv2.medianBlur 來作影像平滑模糊化,在寫 Python 影像處理程式時常會用到 OpenCV 圖片平滑模糊化的功能,而中值濾波 Median Filtering 是其中一個方法,接下來介紹怎麼使用中值濾波 cv2.medianBlur 來進行影像平滑模糊化。

中值濾波 Median Filtering

這邊我們介紹中值濾波 Median Filtering,使用 cv2.medianBlur 就可以計算 kernel 視窗內所有 pixel 的中位數然後取代 kernel 中間的數值,中值濾波 Median Filtering 這個方法對於去除雜訊很有效,我們這邊示範讀取一個有雜訊 opencv logo 的圖片然後做 cv2.medianBlur,kernel 大小為 5,

opencv-medianBlur.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv-logo-noise.png')

blur = cv2.medianBlur(img, 5)

plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

結果如下圖所示:

cv2.medianBlur 參數的詳細細節請參考這裡

參考
OpenCV: Smoothing Images
https://docs.opencv.org/4.x/d4/d13/tutorial_py_filtering.html

其它相關文章推薦
Python OpenCV 影像平滑模糊化 blur
Python OpenCV cv2.GaussianBlur 高斯濾波
Python OpenCV 影像二值化 Image Thresholding
Python OpenCV 彩色轉灰階(RGB/BGR to GRAY)
Python OpenCV 彩色轉HSV(RGB/BGR to HSV)
Python OpenCV 彩色轉YCbCr(RGB/BGR to YCbCr)
Python OpenCV 影像邊緣偵測 Canny Edge Detection
Python OpenCV resize 圖片縮放

Python OpenCV cv2.GaussianBlur 高斯濾波

本篇 ShengYu 將介紹 Python 使用 OpenCV cv2.GaussianBlur 來作影像平滑模糊化,在寫 Python 影像處理程式時常會用到 OpenCV 圖片平滑模糊化的功能,而高斯濾波 Gaussian Filtering 是其中一個方法,接下來介紹怎麼使用高斯濾波 cv2.GaussianBlur 來進行影像平滑模糊化。

cv2.GaussianBlur 高斯濾波

這邊我們介紹高斯濾波 Gaussian Filtering,它與平均濾波 Averaging 類似,平均濾波 Averaging 的 kernel 裡的每個 pixel 權重都是1,而高斯濾波給予每個 pixel 不同權重,中心 pixel 的權重最高,越往邊角權重就越低,相較於平均濾波 Averaging 這樣可以讓圖片失真較少,高斯濾波通常去除雜訊也有不錯的效果。

以下範例為 kernel size 為 5x5,sigma 為 0,

opencv-GaussianBlur.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv-logo.png')

blur = cv2.GaussianBlur(img, (5, 5), 0)

plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

結果如下圖所示:

cv2.GaussianBlur 參數的詳細細節請參考這裡

cv2.getGaussianKernel 取得高斯 kernel

這邊介紹使用 cv2.getGaussianKernel 取得高斯 kernel,

opencv-GaussianBlur2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np

kernel_size = 3
sigma = 0

# get 1-D Gaussian kernel
kernel_1d = cv2.getGaussianKernel(kernel_size, sigma)
print(kernel_1d)
kernel_2d = kernel_1d * kernel_1d.T
print(kernel_2d)

輸出結果如下,

1
2
3
4
5
6
[[0.25]
[0.5 ]
[0.25]]
[[0.0625 0.125 0.0625]
[0.125 0.25 0.125 ]
[0.0625 0.125 0.0625]]

使用 cv2.sepFilter2D 做高斯濾波

這邊介紹使用 cv2.sepFilter2D 來做高斯濾波也能達成 cv2.GaussianBlur 同樣的效果,先使用 cv2.getGaussianKernel 建立一個 1-D 的 kernel,接著使用 cv2.sepFilter2D 且分別將 kernelX 與 kernelY 參數都設定成剛剛建立好的 1-D 的 kernel,這樣的結果跟 cv2.GaussianBlur 結果一樣。

opencv-GaussianBlur3.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('opencv-logo.png')

kernel_size = 5
sigma = 0
kernel_1d = cv2.getGaussianKernel(kernel_size, sigma)

blur = cv2.sepFilter2D(img, -1, kernel_1d, kernel_1d)

plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

cv2.sepFilter2D 參數的詳細細節請參考這裡

以上就是 Python OpenCV cv2.GaussianBlur 高斯濾波介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

參考
OpenCV: Smoothing Images
https://docs.opencv.org/4.x/d4/d13/tutorial_py_filtering.html
[Python]Gaussian Filter-概念與實作. 實作 Gaussian Filter
https://medium.com/@bob800530/python-gaussian-filter-%E6%A6%82%E5%BF%B5%E8%88%87%E5%AF%A6%E4%BD%9C-676aac52ea17
How Blurs & Filters Work - Computerphile
https://youtu.be/C_zFhWdM4ic
opencv 高斯核是怎么通过参数ksize和sigma计算得到的 cv2.getGaussianKernel()
https://blog.csdn.net/weixin_37804469/article/details/113843829
Gaussian Blurring | TheAILearner
https://theailearner.com/2019/05/06/gaussian-blurring/

其它相關文章推薦
Python OpenCV 影像平滑模糊化 blur
Python OpenCV cv2.medianBlur 中值濾波
Python OpenCV 影像二值化 Image Thresholding
Python OpenCV 彩色轉灰階(RGB/BGR to GRAY)
Python OpenCV 彩色轉HSV(RGB/BGR to HSV)
Python OpenCV 彩色轉YCbCr(RGB/BGR to YCbCr)
Python OpenCV 影像邊緣偵測 Canny Edge Detection
Python OpenCV resize 圖片縮放

Python print numpy array 設定精準度

本篇紀錄如何使用 Python print numpy array 設定精準度。

Python print numpy array 設定精準度

以下範例示範 Python print numpy array 設定精準度,第一個 arr1 是隨機產生 3 個數值,第二個 arr2 是指定各種數值並且有一些是含科學符號的數值,使用 np.printoptions() 設定 precision 參數可以控制印出的精準度,如果不想要影響全域結果可以使用區域的寫法 with np.printoptions(...): (需要 NumPy 1.15.0 或以後的版本),

python3-numpy-print-array-precision.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np

arr1 = np.random.random(3)
print(arr1)
#np.printoptions(precision=2)
with np.printoptions(precision=2, suppress=True):
print(arr1)
print(arr1)

arr2 = np.asarray([1.5e-10, 1.5, 1500])

print(type(arr2)) # <class 'numpy.ndarray'>
print(arr2)
#np.printoptions(precision=2)
with np.printoptions(precision=2, suppress=True):
print(arr2)
print(arr2)

輸出如下,

1
2
3
4
5
6
7
[0.99005858 0.2245673  0.5895889 ]
[0.99 0.22 0.59]
[0.99005858 0.2245673 0.5895889 ]
<class 'numpy.ndarray'>
[1.5e-10 1.5e+00 1.5e+03]
[ 0. 1.5 1500. ]
[1.5e-10 1.5e+00 1.5e+03]

其他參考
python - How to pretty-print a numpy.array without scientific notation and with given precision? - Stack Overflow
https://stackoverflow.com/questions/2891790/how-to-pretty-print-a-numpy-array-without-scientific-notation-and-with-given-pre

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 新手入門教學懶人包
Python str 字串用法與範例
Python list 串列用法與範例
Python set 集合用法與範例
Python dict 字典用法與範例
Python tuple 元組用法與範例

Python numpy 寫入 csv

本篇紀錄如何使用 python numpy 的資料寫入 csv。

將 numpy array 用 savetxt 寫入 csv

以下範例將 numpy array 寫入 csv,

python3-numpy-csv-write.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np

arr = np.asarray([
[1,2,3],
[4,5,6],
[7,8,9]
])

print(type(arr)) # <class 'numpy.ndarray'>
np.savetxt('output_data.csv', arr, delimiter=',')
#np.savetxt('output_data.csv', arr, delimiter=',', fmt='%d')
#np.savetxt('output_data.csv', arr, delimiter=',', fmt='%.2f')

輸出的 output_data.csv 內容如下,

1
2
3
1.000000000000000000e+00,2.000000000000000000e+00,3.000000000000000000e+00
4.000000000000000000e+00,5.000000000000000000e+00,6.000000000000000000e+00
7.000000000000000000e+00,8.000000000000000000e+00,9.000000000000000000e+00

如果加入 fmt 參數的話可以控制輸出的格式,例如可以控制輸出的精準度 fmt='%d' 為整數 (預設為 fmt='%.18e'),輸出的 output_data.csv 內容如下,

1
2
3
1,2,3
4,5,6
7,8,9

如果是 fmt='%.2f' 為小數點第二位,輸出的 output_data.csv 內容如下,

1
2
3
1.00,2.00,3.00
4.00,5.00,6.00
7.00,8.00,9.00

另外如果是使用 arr.tofile('output_data.csv', sep=',') (numpy.ndarray.tofile) 的方式寫入 csv 是不會換行的。

其他參考
numpy.savetxt — NumPy Manual
https://numpy.org/doc/stable/reference/generated/numpy.savetxt.html
python - Setting the fmt option in numpy.savetxt - Stack Overflow
https://stackoverflow.com/questions/17043393/setting-the-fmt-option-in-numpy-savetxt

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 寫入 csv 檔案
Python 新手入門教學懶人包
Python str 字串用法與範例
Python list 串列用法與範例
Python set 集合用法與範例
Python dict 字典用法與範例
Python tuple 元組用法與範例

macOS 使用 pip 安裝 opencv

本篇記錄一下 macOS 使用 pip 安裝 opencv,安裝完後就可以開始用 python 開法 opencv 影像處理程式囉!

python pip 安裝 opencv-python 的指令如下,預設是安裝最新版,

1
pip3 install opencv-python

我的 macOS 是 10.13.4,我試過不論是 opencv 4 最新版或 opencv 3 最新版在我的環境下會編譯失敗顯示這個問題,

1
2
3
$ pip3 install opencv-python
...
ERROR: Could not build wheels for opencv-python, which is required to install pyproject.toml-based projects

上網找了一下資料,除了把 macOS 版本更新到最新的方法以外,還可以試試安裝舊版 opencv,所以新版 macOS 應該沒有這個問題,我暫時還不想升級 macOS,中間我試過升級 pip 工具 pip3 install --upgrade pip setuptools wheel,還有安裝 python 3.9 brew install python@3.9 (我原本使用的是 python 3.7) 都無法解決這個問題,

最後安裝 opencv 舊版本才順利成功,使用 pip 安裝的話大約是 2021/1/4 以後的版本都會抓 source code 下來 build,舊版本才會抓預編譯的 whl 檔,

1
2
pip3 install opencv-python==3.4.13.47 # opencv 3
pip3 install opencv-python==4.5.1.48 # opencv 4

如果要安裝 contrib 的話則是,

1
2
pip3 install opencv-contrib-python==3.4.13.47 # opencv 3
pip3 install opencv-contrib-python==4.5.1.48 # opencv 4

參考
docker - ERROR: Could not build wheels for opencv-python which use PEP 517 and cannot be installed directly - Stack Overflow
https://stackoverflow.com/questions/63732353/error-could-not-build-wheels-for-opencv-python-which-use-pep-517-and-cannot-be

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 新手入門教學懶人包
Python 安裝 OpenCV 模組
Python pip install 如何安裝指定版本的套件
其他 macOS 相關文章,
macOS 查詢 Xcode 版本的 3 種方法
mac 雙螢幕延伸模式的 dock 切換方法
macOS Screen Sharing 用指令開啟螢幕分享
macOS 安裝舊版的 Xcode 版本