Android adb logcat 出現 read unexpected eof 解決方法

在 Android 下使用 adb logcat 跑一跑出現 read unexpected eof 就停了,該如何解決呢?

有網友發現透過設定 adb logcat -v time 以解決問題,可以參考官網的輸出格式
如果是這樣的話那就表示 log 量太多了,logcat 來不及處理,

解決方法

最後是調整 adb logcat 的 buffer size 解決的,
設定 logcat 的 buffer size 可以透過設定選單裡的開發人員選項去設定,

1
2
Settings > System > Developer options > Logger buffer sizes
設定 > 系統 > 開發人員選項 > 緩衝區日誌大小

也可以透過指令去設定,因為用指令比較迅速,所以這邊介紹指令的使用方式,

1
adb logcat -G 100M

這樣表示設定 buffer size 為 100M,預設似乎是 1M

另外也可以下指令確認一下目前 adb logcat 的 buffer size,
取得的 adb logcat 的 buffer size 指令為

1
adb logcat -g

參考
Android - 输出Logcat时报错[ read: unexpected EOF! ] - 點部落
https://www.dotblogs.com.tw/IamShawn/2020/06/30/134640
android - Logcat crashes with error: unexpected EOF - Stack Overflow
https://stackoverflow.com/questions/48689930/logcat-crashes-with-error-unexpected-eof
解决 Android 输出Logcat时报错[ read: unexpected EOF! ] - 简书
https://www.jianshu.com/p/4679f7bfac3a
Android下打印出现read: Unexpected EOF!分析_TSZ0000的博客-CSDN博客
https://blog.csdn.net/TSZ0000/article/details/81115875

其他技巧推薦

如果你想學習 adb logcat 還有什麼其他指令的話可以看看我之前寫的Android adb logcat 基本用法教學
其他的 Android 系列文章可以看這篇

Python 寫入 ini 設定檔 ConfigParser 用法教學

本篇介紹如何在 Python 寫入 ini 設定檔,在上一篇我們已經學習過Python 怎麼讀取 ini 檔,這篇我們就來講講怎麼用內建的 configparser 模組來寫入 .ini 設定檔。

Python 寫入 ini 設定檔

在 python 中我們想要將一些程式執行過程中的一些設定檔給儲存下來以便下次利用時,我們可以使用 ini 設定檔來儲存,

以下這個範例是示範將 host 與 port 給寫入到 ini 檔,而且他們兩個都是在http 這個 section 之下,當然你也可以建立很多個 section下面存放各種資料,這邊只先示範一個 section 的範例,

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

host = 'https://www.google.com'
port = 80

config = configparser.ConfigParser()
config['http'] = {}
config['http']['host'] = host
config['http']['port'] = str(port)

with open('config.ini', 'w') as f:
config.write(f)

程式執行完後就可以看見目錄下會輸出一個 config.ini 的檔案,內容如下,

config.ini
1
2
3
[http]
host = https://www.google.com
port = 80

以上就是 Python 寫入 ini 設定檔的方法,

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 新手入門教學懶人包
Python 讀取 ini 設定檔 ConfigParser 用法教學
Python 讀取 txt 文字檔
Python 讀取 csv 檔案
Python 寫入 csv 檔案
Python str 字串用法與範例
Python list 串列用法與範例
Python set 集合用法與範例
Python dict 字典用法與範例
Python tuple 元組用法與範例
Python 字串分割 split
Python 取代字元或取代字串 replace
Python 讓程式 sleep 延遲暫停時間
Python 產生 random 隨機不重複的數字 list
Python PyAutoGUI 使用教學
Python OpenCV resize 圖片縮放

Python tkinter filedialog 開啟檔案對話框

本篇介紹如何在 Python filedialog 中開啟檔案對話框用法與範例,這邊使用 python 內建的 tkinter 的 filedialog 為示範,以下的 Python tkinter filedialog 開啟檔案對話框用法與範例將分為這幾部分,

  • tkinter filedialog 開啟檔案對話框
  • tkinter filedialog 判斷開啟檔案對話框回傳的檔案
  • tkinter filedialog 設定開啟檔案對話框的標題
  • tkinter filedialog 指定一個初始的目錄來開啟檔案
  • tkinter filedialog 設定開啟的檔案類型

那就開始吧!

tkinter filedialog 開啟檔案對話框

Python tkinter 要叫出開啟檔案對話框要使用 filedialog.askopenfilename(),最簡單的使用方法像這樣

1
file_path = filedialog.askopenfilename()

完整的 python 3 使用範例如下,

python3-filedialog-askopenfilename.py
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import filedialog

root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
print(file_path)

如果是 python 2 的話稍微有點不同,不過大同小異,

python-tkFileDialog-askopenfilename.py
1
2
3
4
5
6
7
8
#!/usr/bin/python
# -*- coding: utf-8 -*-
import Tkinter, tkFileDialog

root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
print(file_path)

接著就把程式執行起來看看吧!程式啟動後會彈出一個開啟檔案的對話框,如下圖所示,

接著選好檔案後,按下OK確定按鈕後 askopenfilename() 就會回傳檔案路徑了。

tkinter filedialog 判斷開啟檔案對話框回傳的檔案

通常程式會需要去判斷使用者是否選擇了一個合法的檔案或者是取消動作根本沒選檔案,
這邊示範最簡單的方法是接著判斷檔案是否為空,不為空的話才繼續做接下來的程式邏輯,
filedialog.askopenfilename() 會回傳的類型為 str,若取消的話會回傳一個空的 tuple ()
判斷 tuple 不為 empty 的寫法可以用 not 運算子,之前在這篇有介紹過,
實際上程式會寫成這樣,

python3-filedialog-askopenfilename2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import filedialog

root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()

if not file_path:
print('file path is empty')
else:
with open(file_path, 'r') as f:
print(f.read())

tkinter filedialog 設定開啟檔案對話框的標題

filedialog 想要設定一些提示訊息在開啟檔案對話框的標題,來提示使用者到底要開什麼檔案的話,
可以在 filedialog.askopenfilename() 參數裡指定 title
實際上就會寫成這樣,

python3-filedialog-askopenfilename3.py
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import filedialog

root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(parent=root,
title='Select file')

tkinter filedialog 指定一個初始的目錄來開啟檔案

通常會有個初始的目錄讓使用者去選,但預設的目錄可能離最終目標的目錄差很多層,這樣使用者要點很多次,很不方便,所以會給一個初始目錄,

python3-filedialog-askopenfilename4.py
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import filedialog

root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(parent=root,
initialdir='~/')

tkinter filedialog 設定開啟的檔案類型

假設使用者只想開啟圖片類型的檔案,又不想看到一堆非圖片類型的檔案例如像 .txt 或其他類型,
否則使用者在選擇檔案時會找很慢,所以有些情況下會去設定開啟的檔案類型,這有助於加速使用者開啟檔案,
可以在 filedialog.askopenfilename() 參數裡指定 filetypes,像這樣

python3-filedialog-askopenfilename5.py
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import filedialog

root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(parent=root,
filetypes = (("jpeg files","*.jpg"),("all files","*.*")))

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

其它參考
openfiledialog - Quick and easy file dialog in Python? - Stack Overflow
https://stackoverflow.com/questions/9319317/quick-and-easy-file-dialog-in-python
Tkinter Dialogs — Python 3 documentation
https://docs.python.org/3/library/dialog.html
Tkinter tkFileDialog module - Python Tutorial
https://pythonspot.com/tk-file-dialogs/

其它相關文章推薦
Python tkinter 選擇資料夾對話框
Python 新手入門教學懶人包
Python tkinter 新手入門教學

Python 讀取 ini 設定檔 ConfigParser 用法教學

本篇介紹如何在 python 讀取 ini 設定檔,並解析 .ini 檔裡的每個屬性欄位的數值,在開始前我們先來認識一下什麼是ini檔案格式。

什麼是 INI 檔案格式

INI 檔案格式是應用在電腦軟體上的設定檔,是以一種以文字所組成的特殊結構語法,其內容可能有多個section區段,且每個 section 區段都有各自的多個屬性,每個屬性都是 key-value 為一組所構成,
起初 ini 檔被廣泛應用在 Windows 作業系統中,這個格式後來變成了一種非正式的標準並且被其他作業系統與應用程式當設定檔儲存使用,另外有些應用程式可能改用.conf.cfg這種副檔名。

簡單的 .ini 設定檔內容如下所示,

config.ini
1
2
3
[http]
host = https://www.google.com
port = 80

Python 讀取 ini 設定檔

以上述的 config.ini 為例,我們要寫一個可以讀取這個 ini 設定檔的 python 程式,必須要能解析 ini 檔的內容結構,而 python 內建已經提供了 configparser 模組替我們完成這件事,所以我們只需學習會使用即可。

在 python 3 使用時需要先 import configparser
在 python 2 使用時需要先 import Configparser
一開始建立完一個 configparser 物件後使用 config.read() 將 ini 檔內容讀取進來,之後再用字典的方式來取得資料,

從 ini 檔讀取進來的格式一律都是字串,所以要讀取數字的話需要將讀取進來的字串轉成數字,例如讀取整數要將讀取的字串用 int() 轉換,

以下以 python 3 作示範,

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

config = configparser.ConfigParser()
config.read('config.ini')

host = config['http']['host']
port = int(config['http']['port'])
print(host)
print(port)

結果輸出:

1
2
https://www.google.com
80

configparser 也有提供直接轉成 int 的函式叫 getint() 實際用法如下所示,這樣使用後就直接可以取得 int 整數了,類似的型別還有 getfloat()getboolean() 來轉換 float 與 bool,

1
port = config['http'].getint('port')

下一篇將介紹 Python 如何寫入ini 設定檔

以上就是 Python 讀取 ini 設定檔 ConfigParser 用法教學介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

參考
Python 設定檔解析模組 ConfigParser 使用教學 - Office 指南
https://officeguide.cc/configparser-python-configuration-file-parser-tutorial/
使用python讀寫ini配置文檔 | Code . Arts . Travel
http://www.jysblog.com/coding/python/python-%E8%AE%80%E5%AF%ABini%E9%85%8D%E7%BD%AE%E6%96%87%E6%AA%94/
How to read a config file using python - Stack Overflow
https://stackoverflow.com/questions/19379120/how-to-read-a-config-file-using-python
python - How to read and write INI file with Python3? - Stack Overflow
https://stackoverflow.com/questions/8884188/how-to-read-and-write-ini-file-with-python3

其它相關文章推薦
如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 新手入門教學懶人包
Python 寫入 ini 設定檔 ConfigParser 用法教學
Python 讀取 txt 文字檔
Python 讀取 csv 檔案
Python 寫入 csv 檔案
Python str 字串用法與範例
Python list 串列用法與範例
Python set 集合用法與範例
Python dict 字典用法與範例
Python tuple 元組用法與範例
Python 字串分割 split
Python 取代字元或取代字串 replace
Python 讓程式 sleep 延遲暫停時間
Python 產生 random 隨機不重複的數字 list
Python PyAutoGUI 使用教學
Python OpenCV resize 圖片縮放

Python 檢查 tuple 是否為空

本篇介紹如何在 python 檢查 tuple 是否為空,

使用 not operator 運算子

使用 not operator 運算子來檢查 tuple 是否為空,
同時也是官方推薦的作法,

1
2
3
4
5
6
7
8
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
mytuple = () # or mytuple = tuple()
print(type(mytuple))
print(mytuple)

if not mytuple:
print('mytuple is empty')

結果輸出:

1
2
3
<class 'tuple'>
()
mytuple is empty

使用 len 判斷長度

使用 len() 函式來檢查 tuple 是否為空,

1
2
3
4
5
6
7
8
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
mytuple = tuple() # or mytuple = ()
print(type(mytuple))
print(len(mytuple))

if len(mytuple) == 0:
print('mytuple is empty')

結果輸出:

1
2
3
<class 'tuple'>
0
mytuple is empty

參考
python - Why can’t I detect that the tuple is empty? - Stack Overflow
https://stackoverflow.com/questions/23370921/why-cant-i-detect-that-the-tuple-is-empty
python - How do I check if a list is empty? - Stack Overflow
https://stackoverflow.com/questions/53513/how-do-i-check-if-a-list-is-empty
Python: Check if Tuple is Empty
https://www.stechies.com/amp/check-python-tupleis-empty/

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

Python OpenCV 裁剪圖片 crop

本篇介紹如何在 python opencv crop 裁剪圖片,

使用陣列索引裁剪圖片

在 python 中裁切圖片可以利用 numpy array 陣列索引取出想要的範圍,達成我們要裁剪影像的目的,
語法如下,

1
cropped = image[y:y+h,x:x+w]

第一個參數為y,也就是高度,第二個參數為x,也就是寬度,這樣就可以裁剪出(x, y)左上座標到(x+w, y+h)右下座標範圍之間的圖片了
來看看完整程式怎麼寫吧!
這邊我們裁剪 (0,0) 左上座標到 (512,128) 右下座標範圍的影像為例,

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

img = cv2.imread('lena.jpg')
print(img.shape)
print(type(img))

cropped = img[0:128, 0:512]
cv2.imshow('image', img)
cv2.imshow('cropped', cropped)
cv2.waitKey(0)

程式輸出如下,

1
2
(512, 512, 3)
<class 'numpy.ndarray'>

結果就像下圖這樣,用另一個視窗顯示裁剪出來的圖片,

那再裁剪一個影像看看,這次我們裁剪 (128,0) 左上座標到 (512,256) 右下座標範圍的影像

1
cropped = img[0:256, 128:512]

把裁剪圖跟原圖放一起比對,確認一下,

參考
How to crop an image in OpenCV using Python - Stack Overflow
https://stackoverflow.com/questions/15589517/how-to-crop-an-image-in-opencv-using-python
Python 與 OpenCV 裁切圖片教學 - G. T. Wang
https://blog.gtwang.org/programming/how-to-crop-an-image-in-opencv-using-python/
Basic Image Manipulations in Python and OpenCV: Resizing (scaling), Rotating, and Cropping - PyImageSearch
https://www.pyimagesearch.com/2014/01/20/basic-image-manipulations-in-python-and-opencv-resizing-scaling-rotating-and-cropping/
Python实现图片裁剪的两种方式——Pillow和OpenCV_MrLittleDog的博客-CSDN博客_python 图片裁剪
https://blog.csdn.net/hfutdog/article/details/82351549

其它相關文章推薦
如果你在學習 Python 或 OpenCV 影像處理相關技術,可以參考看看下面的文章,
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 顯示camera攝影機串流影像
Python OpenCV 垂直vconcat 和水平hconcat 影像拼接
Python 旋轉圖片 rotate
Python 圖片模糊化 blur
Python 新手入門教學懶人包
小專案 Python OpenCV 圖片轉字元圖畫

Python xor 運算子用法與範例

本篇將介紹如何在 Python 中使用 xor 位元運算子(bitwise operator)用法與範例,

python xor 運算子

在 python 中 XOR 位元運算要用 ^ 來表示,

如果還沒學習過 XOR 或忘記 XOR 的運算結果的話,可以參考下方的 XOR 的真值表,

1
2
3
4
5
6
a | b | a ^ b
--|---|------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0

那麼就馬上來練習看看 python XOR 怎麼寫囉!

python3-xor.py
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
a = 0
a = a ^ 1
print(a)
a = a ^ 1
print(a)
a ^= 1
print(a)

可以對照上面的真值表看看,程式結果輸出如下:

1
2
3
1
0
1

下一篇介紹三元運算子的用法

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

參考
python - What does the ^ (XOR) operator do? - Stack Overflow
https://stackoverflow.com/questions/14526584/what-does-the-xor-operator-do/14526640
^ Bitwise Exclusive XOR — Python Reference (The Right Way) 0.1 documentation
https://python-reference.readthedocs.io/en/latest/docs/operators/bitwise_XOR.html

其它相關推薦文章

如果你想學習 Python 相關技術,可以參考看看下面的文章,
Python 取得鍵盤輸入 input
Python for 迴圈
Python list 串列
Python dict 字典
Python sort 排序
Python 建立多執行緒 thread
Python 讀檔,讀取 txt 文字檔
Python PIL 讀取圖片並顯示
Python OpenCV resize 圖片縮放

Python 計算 md5 hash 雜湊值

本篇介紹如何用 Python 來計算 md5 hash 雜湊值,以下內容將會包含使用 python 對字串作 md5 hash 的計算和對檔案內容作 md5 hash 的計算,
md5 是一種被廣泛使用的密碼雜湊函式,可以產生出一個長度為 128 bit 位元的 hash 雜湊值,用於確保資訊傳輸完整一致。
hash 的演算法有很多種,例如:SHA-1, SHA-2, MD5 等等,安全強度各有不同,依照不同情況來使用,
由於 hash 雜湊演算法所計算出來的 hash 雜湊值具有不可逆與低碰撞的特性,因此延伸出許多應用,
hash 的應用有很多種,我們今天將介紹其中兩種,第一種「儲存使用者密碼與判斷使用輸入的密碼是否相同」,第二種是「比較檔案內容是否相同」,

在 python 中要使用 md5 計算 hash 雜湊值的話,需要先 import hashlib 模組,這個 hashlib 模組是內建的,所以你不需要安裝額外的模組就可以進行以下的內容,

計算字串的 md5 hash 雜湊值

因為 hash 雜湊演算法的不可逆特性,所以常被應用在資料庫中使用者密碼的存儲,
以一個 Hello world 字串當作密碼為例,
python 3 對這個字串作 md5 hash 計算的範例如下,

1
2
3
4
5
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib

print(hashlib.md5('Hello world'.encode('utf-8')).hexdigest())

輸出結果如下所示,針對同一個字串每次計算的結果會相同,不同電腦也是,

1
3e25960a79dbc69b674cd4ec67a72c62

我們只需在資料庫存放 hash 密碼後的雜湊值,之後使用者在輸入密碼驗證時,
比對 hash 輸入密碼後的雜湊值是否與資料庫存放的雜湊值相同即可,比對結果相同表示使用者極高地可能是輸入同一組密碼,
為什麼說「極高地可能」呢?取決於hash演算法的碰撞機率,這先暫時忽略不在本篇所要討論範圍內,
如此一來我們就不必冒著安全風險將使用者密碼的明碼存在資料庫內了,

Python 2 與 Python 3 的 hashlib.md5 用法略有不同,python 2 的用法如下,

1
2
3
4
5
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib

print(hashlib.md5('Hello world').hexdigest())

不同的原因是因為 hashlib.md5() 在 python 3 是接受一個 byte 變數類型,在 python 2 是接受一個 str 變數類型,
如果在 python 3 中沒有將 str 轉為 byte 的話將會看到下列錯誤訊息

1
TypeError: Unicode-objects must be encoded before hashing

知道 hashlib.md5() 在 python 2/3 差異後,以下內容將示範 python3 的寫法,而且官方已經宣佈終止 python2 的支援了,
所以寫成函式的話就會像下列這樣使用,

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

def hash(s):
ret = hashlib.md5(s.encode())
return ret.hexdigest()

str2hash = 'Hello world'
print('md5 hash: ' + hash(str2hash))

計算檔案內容的 md5 hash 雜湊值

用 md5 計算檔案內容的 hash 雜湊值算是蠻常見的應用,舉例來說,網路上常有一些分享的程式或檔案,
但也有可能這些檔案被駭客給修改過了,甚至植入了病毒,那麼我們如果知道原始檔案的md5值,
就能夠將這個下載的檔案驗證一下,是不是計算出來的md5值也是如同原始檔的一樣,
利用這個原理,我們也能夠去計算兩個不同檔案名稱的檔案內容是否為一樣,

那麼接下來我們就來示範一下如何計算檔案內容的 md5 hash 雜湊值
hashlib.md5() 時就帶入資料

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

def hashfile(filename):
with open(filename, "rb") as f:
buf = f.read()
m = hashlib.md5(buf)
return m.hexdigest()

print('md5 hash: ' + hashfile('lena.jpg'))

另一種是分批讀取檔案內容,先初始化 hashlib.md5() 再使用 update() 來更新新的資料,
分批讀取跟上面的方法比不一定比較快,但有些特殊情形會這樣使用,

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

def hashfile(filename):
m = hashlib.md5()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
m.update(chunk)
return m.hexdigest()

print('md5 hash: ' + hashfile('lena.jpg'))

計算出來的結果應該與 md5sum 指令計算出來的結果相同,
不熟悉 md5sum 指令的話可以回去看我之前的 md5sum 指令用法 這篇文章,

參考
How to get MD5 sum of a string using python? - Stack Overflow
https://stackoverflow.com/questions/5297448/how-to-get-md5-sum-of-a-string-using-python
Python 計算 MD5 與 SHA 雜湊教學與範例 - G. T. Wang
https://blog.gtwang.org/programming/python-md5-sha-hash-functions-tutorial-examples/
MD5 hash in Python - GeeksforGeeks
https://www.geeksforgeeks.org/md5-hash-python/
nicolimo86/python_hash_file.py - github gist
https://gist.github.com/nicolimo86/7536613

延伸閱讀
一次搞懂密碼學中的三兄弟— Encode、Encrypt 跟Hash
https://medium.com/starbugs/what-are-encoding-encrypt-and-hashing-4b03d40e7b0c

其它相關文章推薦
Python 讀取 txt 文字檔,一篇搞懂!
Python OpenCV 彩色轉灰階(RGB/BGR to GRAY)
Python OpenCV 彩色轉HSV(RGB/BGR to HSV)
Python OpenCV 灰階轉彩色(Gray to RGB/BGR)
Python OpenCV 影像邊緣偵測 Canny Edge Detection
Python OpenCV resize 圖片縮放

C++ std::map 用法與範例

本篇將介紹如何使用 C++ std map 以及用法,C++ std::map 是一個關聯式容器,關聯式容器把鍵值和一個元素連繫起來,並使用該鍵值來尋找元素、插入元素和刪除元素等操作。

map 是有排序關聯式容器,即 map 容器中所有的元素都會根據元素對應的鍵值來排序,而鍵值 key 是唯一值,並不會出現同樣的鍵值 key,也就是說假設已經有一個 鍵值 key 存在 map 裡,當同樣的鍵值 key 再 insert 資料時,新的資料就會覆蓋掉原本 key 的資料。

map 的實作方式通常是用紅黑樹(red-black tree)實作的,這樣它可以保證可以在O(log n)時間內完成搜尋、插入、刪除,n為元素的數目。

以下內容將分為這幾部分,

  • map 常用功能
  • map 初始化
  • map 容器插入元素與存取元素
  • map 容器的迴圈遍歷
  • 使用 string 當 key 鍵值, int 當 value 資料的 map 範例
  • 使用 string 當 key 鍵值, 自定義類別或自定義結構當 value 資料的 map 範例
  • 刪除 map 指定的元素
  • 清空 map 容器
  • 判斷 map 容器是否為空

要使用 map 容器的話,需要引入的標頭檔<map>

map 常用功能

C++ map 是一種關聯式容器,包含「key鍵值/value資料」成對關係
元素存取
operator[]:存取指定的[i]元素的資料
迭代器
begin():回傳指向map頭部元素的迭代器
end():回傳指向map末尾的迭代器
rbegin():回傳一個指向map尾部的反向迭代器
rend():回傳一個指向map頭部的反向迭代器
容量
empty():檢查容器是否為空,空則回傳true
size():回傳元素數量
max_size():回傳可以容納的最大元素個數
修改器
clear():刪除所有元素
insert():插入元素
erase():刪除一個元素
swap():交換兩個map
查找
count():回傳指定元素出現的次數
find():查找一個元素

map 初始化

接下來說說怎麼初始化 c++ map 容器吧!
先以 int 當 key, string 當 value 的 map 為範例,
以一個班級的學生編號為例,一個編號會對應到一個學生,這邊先以學生姓名作示範,
所以會寫成std::map<int, std::string> studentMap這樣
std::map 宣告時要宣告兩個變數類型,
map.first:第一個稱為(key)鍵值,在 map 裡面,(key)鍵值不會重複
map.second:第二個稱為(key)鍵值對應的數值(value)
map 容器初始化的寫法如下,

1
2
3
4
std::map<int, std::string> studentMap;
studentMap.insert(std::pair<int, std::string>(1, "Tom"));
studentMap.insert(std::pair<int, std::string>(7, "Jack"));
studentMap.insert(std::pair<int, std::string>(15, "John"));

或者像陣列的方式一樣

1
2
3
4
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";

或者這樣初始化更專業一點

1
2
3
4
5
std::map<int, std::string> studentMap = {
{1, "Tom"},
{2, "Jack"},
{3, "John"}
};

map 容器插入元素與存取元素

剛剛在初始化部分已經有示範過了,map 容器插入元素有兩種寫法,
第一種是使用中括號 [] 的方式來插入元素,很像陣列的寫法,例如:map[key] = value,如果該 key 值已經存在則 value 會被更新成新的數值,範例如下,

1
2
3
4
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
...
studentMap[1] = "John";

另一種是使用 map.insert() 成員函式來插入元素,
那這個 map.insert() 方式跟上面中括號 [] 的方式有什麼不同呢?

差別在於如果 key 值已經存在的話,使用中括號 [] 的方式會將新資料覆蓋舊資料,
而使用 map.insert() 的方式會回傳插入的結果,該 key 值存在的話會回傳失敗的結果,
檢查 map.insert() 的插入結果方式如下,

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

int main() {
std::map<int, std::string> studentMap;
studentMap.insert(std::pair<int, std::string>(1, "Tom"));

std::pair<std::map<int, std::string>::iterator, bool> retPair;
retPair = studentMap.insert(std::pair<int, std::string>(1, "Tom"));

if (retPair.second == true)
std::cout << "Insert Successfully\n";
else
std::cout << "Insert Failure\n";

return 0;
}

要取得某 key 鍵值元素的話可以這樣寫,或者看下節遍歷整個 map 容器

1
std::cout << "name: " << studentMap[1] << "\n";

map 容器的迴圈遍歷

迴圈遍歷 map 容器的方式有幾種,以下先介紹使用 range-based for loop 來遍歷 map 容器,
這邊故意將 id 不按順序初始化或者插入,先初始化 132 key 鍵值的元素,
之後再插入 54 key 鍵值的元素,然後我們再來觀察看看是不是 map 會將其排序,

std-map2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ std-map2.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>

int main() {
std::map<int, std::string> studentMap = {
{1, "Tom"},
{3, "John"},
{2, "Jack"}
};
studentMap[5] = "Tiffany";
studentMap[4] = "Ann";

for (const auto& s : studentMap) {
std::cout << "id: " << s.first << ", name: " << s.second << "\n";
}
return 0;
}

輸出內容如下,從這個輸出結果發現是 key 鍵值由小到大排列,所以 map 容器裡面真的是會幫你排序的,
在插入元素的同時會根據鍵值來進行排序,

1
2
3
4
5
id: 1, name: Tom
id: 2, name: Jack
id: 3, name: John
id: 4, name: Ann
id: 5, name: Tiffany

使用前向迭代器,輸出結果跟上列相同,

1
2
3
4
5
for (std::map<int, std::string>::iterator it = studentMap.begin(); it != studentMap.end(); it++) {
// or
// for (auto it = studentMap.begin(); it != studentMap.end(); it++) {
std::cout << "id: " << (*it).first << ", name: " << (*it).second << "\n";
}

使用反向迭代器的例子,如果嫌 iterator 迭代器名稱太長的話可以善用 auto 關鍵字讓編譯器去推導該變數類型,

1
2
3
4
5
// for (std::map<int, std::string>::reverse_iterator it = studentMap.rbegin(); it != studentMap.rend(); it++) {
// or
for (auto it = studentMap.rbegin(); it != studentMap.rend(); it++) {
std::cout << "id: " << (*it).first << ", name: " << (*it).second << "\n";
}

反向迭代器的輸出結果如下,反著印出來,

1
2
3
4
5
id: 5, name: Tiffany
id: 4, name: Ann
id: 3, name: John
id: 2, name: Jack
id: 1, name: Tom

使用 string 當 key 鍵值, int 當 value 資料的 map 範例

這個範例跟之前的範例相反,我們試著用 string 當作 key 鍵值,int 當 value,看看是不是也可以這樣使用,
所以會寫成 std::map<std::string, int> 這樣,完整範例如下,

std-map3.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
// g++ std-map3.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>

int main() {
std::map<std::string, int> map = {
{"Tom", 1},
{"Jack", 2},
{"Ann", 4}
};

for (const auto& n : map) {
std::cout << "key: " << n.first << " value: " << n.second << "\n";
}

map["Tiffany"] = 5;
map["John"] = 3;

std::cout << map["John"] << "\n";
std::cout << map["Tiffany"] << "\n";

for (const auto& n : map) {
std::cout << "key: " << n.first << " value: " << n.second << "\n";
}

return 0;
}

輸出內容如下,可以發現印出來的順序是按照學生姓名的順序,

1
2
3
4
5
6
7
8
9
10
key: Ann value: 4
key: Jack value: 2
key: Tom value: 1
3
5
key: Ann value: 4
key: Jack value: 2
key: John value: 3
key: Tiffany value: 5
key: Tom value: 1

使用 string 當 key 鍵值, 自定義類別或自定義結構當 value 資料的 map 範例

如果要在 value 欄位放入自定義 struct 或 自定義 class 的話,可以參考下面這個範例,
那個宣告就會是 std::map<std::string, Class/Struct> 這樣子寫,

std-map4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// g++ std-map4.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>

struct Student {
int id;
std::string name;
int age;
};

int main() {
std::map<std::string, Student> studentMap;
studentMap["Tom"] = {1, "Tom", 18};
studentMap["Ann"] = {4, "Ann", 20};
studentMap["Jack"] = {2, "Jack", 16};

for (const auto& m : studentMap) {
std::cout << "name: " << m.first << " id: " << m.second.id << " age: " << m.second.age << "\n";
}

return 0;
}

輸出內容如下:

1
2
3
name: Ann id: 4 age: 20
name: Jack id: 2 age: 16
name: Tom id: 1 age: 18

刪除 map 指定的元素

刪除 map 指定的元素要使用 erase()

std-map5.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// g++ std-map5.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>

int main() {
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";

studentMap.erase(1);
for (const auto& m : studentMap) {
std::cout << m.first << " " << m.second << "\n";
}

return 0;
}

結果如下,

1
2
7 Jack
15 John

那如果 map 刪除不存在的元素會發生什麼事呢?

std-map6.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++ std-map6.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>

int main() {
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";

auto ret = studentMap.erase(1);
std::cout << ret << "\n";
for (const auto& m : studentMap) {
std::cout << m.first << " " << m.second << "\n";
}

ret = studentMap.erase(2);
std::cout << ret << "\n";
for (const auto& m : studentMap) {
std::cout << m.first << " " << m.second << "\n";
}

return 0;
}

map 刪除不存在的元素並不會造成什麼 crash 這種嚴重問題,他反而會回傳一個數量告訴你它刪除了多少個元素,以這個例子來說 erase(1) 是刪除了 1 個元素,erase(2) 是刪除了 0 個元素,結果如下,

1
2
3
4
5
6
1
7 Jack
15 John
0
7 Jack
15 John

map erase() 刪除元素還有另外兩種用法,有興趣的可以看這一篇

清空 map 容器

要清空 map 容器的的話,要使用 clear()

1
2
3
4
5
6
std::map<int, std::string> studentMap;
studentMap.insert(std::pair<int, std::string>(1, "Tom"));
studentMap.insert(std::pair<int, std::string>(7, "Jack"));
studentMap.insert(std::pair<int, std::string>(15, "John"));

studentMap.clear();

判斷 map 容器是否為空

要判斷 map 是否為空或是裡面有沒有元素的話,可以用 empty(),寫法如下,

1
2
3
4
5
6
7
8
std::map<int, std::string> studentMap;
studentMap.clear();

if (studentMap.empty()) {
std::cout << "empty\n";
} else {
std::cout << "not empty, size is "<< studentMap.size() <<"\n";
}

參考
std::map - cppreference.com
https://en.cppreference.com/w/cpp/container/map
map - C++ Reference
http://www.cplusplus.com/reference/map/map/
Map in C++ Standard Template Library (STL) - GeeksforGeeks
https://www.geeksforgeeks.org/map-associative-containers-the-c-standard-template-library-stl//
C/C++ - Map (STL) 用法與心得完全攻略 | Mr. Opengate
https://mropengate.blogspot.com/2015/12/cc-map-stl.html
[教學]C++ Map(STL)詳細用法 @ 一個小小工程師的心情抒發天地
http://dangerlover9403.pixnet.net/blog/post/216833943
C++ STL map 的小筆記 @ 伊卡洛斯之翼
http://kamory0931.pixnet.net/blog/post/119251820
C++中的STL中map用法详解 - Boblim - 博客园
https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html
程式扎記: [C++ 範例代碼] 使用 STL 的 map 操作範例
http://puremonkey2010.blogspot.com/2010/08/c-stl-map.html

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
std::multimap 用法與範例
std::unordered_map 用法與範例
std::vector 用法與範例
std::deque 介紹與用法
std::queue 用法與範例

Python Flask OpenCV 攝影機影像即時串流

本篇介紹如何用 Python 與 Flask OpenCV 來建立一個攝影機 MJPEG (Motion JPEG) 即時串流的網站,在網站上顯示 webcam 或 camera 攝影機的即時串流影像,這個應用常見於監控系統或者即時影像處理的情形上。讀取攝影機的影像在這篇使用的是 OpenCV 模組,你也可以改用其它方式來讀取攝影機的影像。你可以使用隨手可得的 webcam,也可以使用一般的 camera 攝影機就可以來試試本篇的教學囉!

學習這篇後你將可以,

  • 建立一個家庭安全監視系統
  • 建立一個嬰兒監視系統
  • 建立一個視訊串流網頁,例如:17直播、Zoom多人線上視訊會議

我陪我老婆在月子中心的時候,就透過月子中心提供的 App 來觀看嬰兒室裡寶寶的最新畫面,同時也可以從網站上觀看,之後我便對此視訊串流技術感到興趣,經過一連串的研究與了解背後的技術是怎麼實現,最後實做完變成了這篇文章。

在建立一個 Flask 串流網站前,需要先了解一個基本的網站是怎麼寫的,要學習建立一個網頁最間單的方式就是從 Hello World 開始,這部分可以參考我之前幾篇 Flask 系列教學,如果你已經學會怎麼建立一個 Flask 網站,接下來就先小試身手,用 Flask 建立一個可以顯示圖片的網站,再一步步進入本篇的主軸。

顯示圖片的 Flask 網頁伺服器

這邊就直接使用 Flask 模板的寫法來處理 index.html,在 index.html 裡放置一張圖片,而圖片這種,

templates/index.html
1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html lang="en">
<head>
<title>Flask</title>
</head>
<body>
<h3>Picture</h3>
<img src="/static/lena.jpg">
</body>
</html>

Flask 裡使用 render_template 處理 templete,

flask-picture.py
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

if __name__ == '__main__':
app.run('0.0.0.0')

Multipart/x-mixed-replace Responses

本篇使用的串流技術是 HTTP 的 multipart 類型回應,瀏覽器透過不同的 Content-Type header 定義可以做出對應的反應,例如瀏覽器遇到 Content-Type 是 application/zip 就下載檔案,遇到 Content-Type 是 application/pdf 就預覽 pdf 等等。

而本篇使用到的 Content-Type 是 multipart/x-mixed-replacex-mixed-replace 是 Server 利用 HTTP 推送串流的技術其中之一,作法是讓每一個資料區塊取代頁面中先前的一塊藉此達到更新畫面,利用這種技術你可以將圖片作為每一個資料區塊來傳送,這樣在瀏覽器上看起來就像在播放視訊或播放動畫的效果。

實作更新的關鍵在於使用 multipart 回應。multipart 回應的內容是先一個 Content-Type 為 multipart/x-mixed-replace 的 header 並指名 boundary=frame 的分割名稱(分割名稱可自行更改),之後的每一個資料區塊的是用 --frame 作為資料區塊分割的標記,並且後面接著 Content-Type 與資料,Content-Type 為 image/jpeg 表示接下來的資料是 jpeg 資料。

以下為資料傳送的示意,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=frame

--frame
Content-Type: image/jpeg

<jpeg data here>
--frame
Content-Type: image/jpeg

<jpeg data here>
--frame
Content-Type: image/jpeg

<jpeg data here>
...

Flask 串流連續影像

在之前的範例已經可以在網頁中呈現一張靜態影像了,接下來我們要來呈現連續影像,用的就是上一節介紹的技術,這邊我們示範連續地循環串流 5 張數字影像,在 index.html 的 img 標籤裡放入 video_feed

templates/index.html
1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html lang="en">
<head>
<title>Flask</title>
</head>
<body>
<h3>Pictures Streaming</h3>
<img src="{{ url_for('video_feed') }}">
</body>
</html>

/video_feed 路由處理

flask-pictures-streaming.py
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template, Response
import time

app = Flask(__name__)
frames = [open(f + '.jpg', 'rb').read() for f in ['1', '2', '3', '4', '5']]

def gen_frames():
counter = 0
while True:
n = counter % 5
print(str(n))
frame = frames[counter % 5]
counter += 1
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
time.sleep(0.5)

@app.route('/video_feed')
def video_feed():
return Response(gen_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/')
def index():
return render_template('index.html')

if __name__ == '__main__':
app.run('0.0.0.0')

開啟 http://0.0.0.0:5000/ 網頁後,應該可以看到網頁上不停地輪流播放 1~5 數字影像,如下圖,

生成器

關於 之前已經介紹過了,

1
2
3
4
def gen_frames():
yield 1
yield 2
yield 3

生成器(generator functions)

Flask camera 攝影機影像串流

Response 傳入一個 genator,
multipart/x-mixed-replace:後續抵達的資料區塊會覆蓋先前的結果,藉此達成動畫效果
boundary=frame:告知後續的連續資料塊以 --frame 作為各單位資料區塊邊界
image/jpeg:告知傳送的每一塊的資料型態為 jpeg 影像

1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html lang="en">
<head>
<title>Flask</title>
</head>
<body>
<h3>Camera Live Streaming</h3>
<img src="{{ url_for('video_feed') }}">
</body>
</html>
flask-camera-streaming.py
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template, Response
import cv2

app = Flask(__name__)
camera = cv2.VideoCapture(0)

def gen_frames():
while True:
success, frame = camera.read()
if not success:
break
else:
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route('/video_feed')
def video_feed():
return Response(gen_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame')

@app.route('/')
def index():
return render_template('index.html')

if __name__ == '__main__':
app.run('0.0.0.0')

app.run() 如果沒有使用 threaded=True 的話,會發現一次只能一個使用者觀看,加上 threaded=True 後 flask 就能夠用多執行緒的方式來服務多個使用者。

執行接下來就可以直接在瀏覽器,開啟 http://127.0.0.1:5000/ 這個網址。

下一篇介紹不使用 Flask 的

其它參考
Video Streaming with Flask - miguelgrinberg.com
https://blog.miguelgrinberg.com/post/video-streaming-with-flask
OpenCV – Stream video to web browser/HTML page
https://www.pyimagesearch.com/2019/09/02/opencv-stream-video-to-web-browser-html-page/
Live Webcam Flask Opencv Python
https://medium.com/@manivannan_data/live-webcam-flask-opencv-python-26a61fee831
Video Streaming in Web Browsers with OpenCV & Flask
https://towardsdatascience.com/video-streaming-in-web-browsers-with-opencv-flask-93a38846fe00
miguelgrinberg / flask-video-streaming
https://github.com/miguelgrinberg/flask-video-streaming
akmamun / camera-live-streaming
https://github.com/akmamun/camera-live-streaming

其它相關文章推薦
Python Flask 建立簡單的網頁
Python Flask render_template 模板

Python Flask 螢幕分享