<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4257432075323421</id><updated>2026-08-29T15:15:54.965-07:00</updated><category term="python"/><category term="dotnet"/><category term="web"/><category term="git"/><category term="c#"/><category term="google"/><category term="點網"/><category term="html5"/><category term="javascript"/><category term="namedpipe"/><category term="編譯器"/><category term="nodejs"/><category term="windows"/><category term="rpi"/><category term="心得"/><category term="超譯"/><category term="atom"/><category term="c/c++"/><category term="electron"/><category term="gae"/><category term="hadoop"/><category term="linux"/><category term="mongodb"/><category term="opennms"/><category term="sql"/><category term="asp"/><category term="ipython"/><category term="jupyter"/><category term="jython"/><category term="sozi"/><category term="ubuntu"/><category term="vscode"/><category term="xml"/><category term="編輯器"/><category term="HP-UX"/><category term="android"/><category term="arduino"/><category term="dulwich"/><category term="golang"/><category term="hg"/><category term="latex"/><category term="mathjax"/><category term="mosquitto"/><category term="mqtt"/><category term="opensource"/><category term="osx"/><category term="sqlite"/><category term="trac"/><category term="vmware"/><category term="多媒體"/><category term="機器學習"/><category term="async"/><category term="await"/><category term="bottle"/><category term="dds"/><category term="debian"/><category term="djangocms"/><category term="docker"/><category term="drivelist"/><category term="flask"/><category term="gevent"/><category term="gluster"/><category term="https"/><category term="iis"/><category term="inkscape"/><category term="linuxkit"/><category term="lxml"/><category term="mezzanine"/><category term="numpy"/><category term="plone"/><category term="pyinstaller"/><category term="raid"/><category term="requests"/><category term="svg"/><category term="tag"/><category term="tornado"/><category term="vbnet"/><category term="web_service"/><category term="webpack"/><category term="xp"/><category term="youtube"/><category term="中文分詞"/><category term="備忘"/><category term="演算法"/><category term="版本控制系統"/><category term="簡報"/><category term="記憶體"/><category term="讀書心得"/><category term="點網核"/><title type='text'>中斷點 之 浩劫與重生</title><subtitle type='html'>記錄寫程式的發現。&#xa;使用 python, C#, VB, java, javascript,&#xa;也會研究 html, xml, web service。</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>271</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-6057565232329447999</id><published>2025-12-30T22:14:00.000-08:00</published><updated>2025-12-30T22:14:22.967-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>[python]計算不定期不定額的年化報酬率</title><content type='html'>&lt;p&gt;&amp;nbsp;## Gemini 解釋 XIRR&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;對於「不定期不定額」的投資，傳統的 CAGR 公式（只看期初和期末）已經不適用了，因為它無法反映資金進出的時間點對獲利的貢獻。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在金融界，這種情況標準的計算方式是 **XIRR (Internal Rate of Return for a schedule of cash flows)**。它考慮了每一筆資金投入的精確日期，並算出一個等效的年化報酬率。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;要在 Python 中計算，最推薦的方式是使用 `pyxirr` 庫，或是手動透過 `scipy` 進行數值求解。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;---&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 1. XIRR 的數學原理&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;XIRR 是在找一個折現率 ，使得所有現金流的現值（Present Value）總和等於零：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;$$0 = \sum_{i=1}^{N} \frac{P_i}{(1+r)^{\frac{d_i - d_1}{365}}}$$&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* $P_i$：第&amp;nbsp; 筆現金流（投入為負數，領回或當前價值為正數）。&lt;/p&gt;&lt;p&gt;* $d_i$：第&amp;nbsp; 筆現金流發生的日期。&lt;/p&gt;&lt;p&gt;* $d_1$：第一筆投資發生的日期。&lt;/p&gt;&lt;p&gt;* $r$：就是我們要找的年化報酬率 (XIRR)。&lt;/p&gt;&lt;p&gt;* $N$：現金流的總次數&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;---&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 2. Python 程式碼實作&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;#### 方法 A：使用 `pyxirr` 庫（最簡單、精準）&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;這是目前處理 XIRR 最推薦的套件，速度快且支援各種格式。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;**安裝：** `pip install pyxirr`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```python&lt;/p&gt;&lt;p&gt;from pyxirr import xirr&lt;/p&gt;&lt;p&gt;from datetime import date&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# 定義現金流與日期&lt;/p&gt;&lt;p&gt;# 注意：投入資金設為「負值」，最後一筆「當前市值」或「贖回金額」設為「正值」&lt;/p&gt;&lt;p&gt;dates = [&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; date(2023, 1, 1),&amp;nbsp; &amp;nbsp;# 第一次投入 10,000&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; date(2023, 4, 15),&amp;nbsp; # 第二次投入 5,000&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; date(2023, 10, 20), # 第三次投入 8,000&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; date(2024, 5, 20)&amp;nbsp; &amp;nbsp;# 當前日期與帳面總價值 (假設現在值 26,000)&lt;/p&gt;&lt;p&gt;]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;amounts = [-10000, -5000, -8000, 26000]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# 計算 XIRR&lt;/p&gt;&lt;p&gt;annual_return = xirr(dates, amounts)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;print(f&quot;不定期不定額的年化報酬率 (XIRR) 為: {annual_return:.2%}&quot;)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;---&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;#### 方法 B：使用 `scipy` 手寫邏輯（不依賴金融庫）&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;如果你不想安裝額外的金融庫，可以用 `scipy` 的優化器來解方程式。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```python&lt;/p&gt;&lt;p&gt;import numpy as np&lt;/p&gt;&lt;p&gt;from scipy.optimize import newton&lt;/p&gt;&lt;p&gt;from datetime import date&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;def calculate_xirr(dates, amounts):&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # 將日期轉換為天數差距&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; days = [(d - dates[0]).days for d in dates]&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # 定義 NPV 函數，我們要求解 NPV = 0 的 r&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; def npv(r):&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return sum(amt / (1 + r) ** (d / 365) for amt, d in zip(amounts, days))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # 使用牛頓法求解，初始猜測值設為 0.1 (10%)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; try:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return newton(npv, 0.1)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; except RuntimeError:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return None # 無法收斂時回傳 None&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# 測試資料&lt;/p&gt;&lt;p&gt;test_dates = [date(2023, 1, 1), date(2023, 6, 1), date(2024, 1, 1)]&lt;/p&gt;&lt;p&gt;test_amounts = [-100, -100, 250] # 投兩次 100，最後拿回 250&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;result = calculate_xirr(test_dates, test_amounts)&lt;/p&gt;&lt;p&gt;print(f&quot;手動計算結果: {result:.2%}&quot;)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;---&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 3. 寫程式時的注意事項&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1. **正負號邏輯：**&lt;/p&gt;&lt;p&gt;* **流出 (投資)：** 必須是**負數**。&lt;/p&gt;&lt;p&gt;* **流入 (提領或結餘)：** 必須是**正數**。如果你還沒賣掉，最後一筆資料就是「今天的日期」和「當前的總市值」。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2. **現金流排序：** 日期建議由遠到近排序，雖然 `pyxirr` 會自動處理，但手寫邏輯時排序能避免計算錯誤。&lt;/p&gt;&lt;p&gt;3. **多重解問題：** 在極少數現金流劇烈震盪的情況下，XIRR 可能會有數學上的多重解，但對於一般的投資行為，數值解通常很穩定。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/6057565232329447999/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2025/12/python.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6057565232329447999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6057565232329447999'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2025/12/python.html' title='[python]計算不定期不定額的年化報酬率'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-2921108624931558793</id><published>2025-12-08T18:00:00.000-08:00</published><updated>2025-12-08T18:00:42.871-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="點網"/><title type='text'>[點網]使用 openxml 從 CSV 檔轉成 excel 檔</title><content type='html'>&lt;p&gt;僅供參考&lt;/p&gt;&lt;p&gt;參考網址:&lt;/p&gt;&lt;p&gt;&amp;nbsp;https://learn.microsoft.com/en-us/office/open-xml/spreadsheet/how-to-create-a-spreadsheet-document-by-providing-a-file-name&lt;/p&gt;&lt;p&gt;https://learn.microsoft.com/en-us/office/open-xml/getting-started&lt;/p&gt;&lt;p&gt;https://www.nuget.org/packages/DocumentFormat.OpenXml&lt;/p&gt;&lt;p&gt;https://blog.darkthread.net/blog/csvhelper/&lt;/p&gt;&lt;p&gt;https://www.cnblogs.com/geovindu/p/19161493&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;程式:&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;DataTable dataTable = ReadCsv(inputPath, encoding, delimiter);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ConvertDataTableToXls(dataTable, outputPath);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;public static DataTable ReadCsv(string filePath, Encoding encoding, char delimiter)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; using (var reader = new StreamReader(filePath, encoding))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; using (var csv = new CsvReader(reader, new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Delimiter = delimiter.ToString(),&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; HasHeaderRecord = true,&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IgnoreBlankLines = true,&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TrimOptions = CsvHelper.Configuration.TrimOptions.Trim&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; using (var dr = new CsvDataReader(csv))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var dt = new DataTable();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; dt.Load(dr);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return dt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; catch (Exception ex)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new Exception($&quot;Failed to read CSV file: {ex.Message}&quot;, ex);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;public static void ConvertDataTableToXls(DataTable dataTable, string outputPath)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Create a new spreadsheet document&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; using (SpreadsheetDocument document = SpreadsheetDocument.Create(outputPath, SpreadsheetDocumentType.Workbook))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Add a WorkbookPart to the document&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; WorkbookPart workbookPart = document.AddWorkbookPart();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; workbookPart.Workbook = new Workbook();&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Add a WorksheetPart to the WorkbookPart&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; WorksheetPart worksheetPart = workbookPart.AddNewPart&amp;lt;WorksheetPart&amp;gt;();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; worksheetPart.Worksheet = new Worksheet(new SheetData());&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Add Sheets to the Workbook&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Append a new worksheet and associate it with the workbook&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Sheet sheet = new Sheet()&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Id = workbookPart.GetIdOfPart(worksheetPart),&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SheetId = 1,&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Name = &quot;Sheet1&quot;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; };&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sheets.Append(sheet);&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Get the SheetData object&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SheetData sheetData = worksheetPart.Worksheet.GetFirstChild&amp;lt;SheetData&amp;gt;();&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Add header row&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Row headerRow = new Row();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foreach (DataColumn column in dataTable.Columns)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Cell cell = CreateCell(column.ColumnName, CellValues.String);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; headerRow.AppendChild(cell);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sheetData.AppendChild(headerRow);&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Add data rows&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foreach (DataRow row in dataTable.Rows)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Row dataRow = new Row();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foreach (var item in row.ItemArray)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CellValues cellType = GetCellValueType(item);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; string cellValue = GetCellValueAsString(item, cellType);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Cell cell = CreateCell(cellValue, cellType);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; dataRow.AppendChild(cell);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sheetData.AppendChild(dataRow);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Save the workbook&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; workbookPart.Workbook.Save();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; catch (Exception ex)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new Exception($&quot;Failed to create XLS file: {ex.Message}&quot;, ex);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private static Cell CreateCell(string value, CellValues cellType)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Cell cell = new Cell();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cell.DataType = new EnumValue&amp;lt;CellValues&amp;gt;(cellType);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cell.CellValue = new CellValue(value);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return cell;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private static CellValues GetCellValueType(object value)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (value == DBNull.Value)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return CellValues.String;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Type type = value.GetType();&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (type == typeof(int) || type == typeof(long) || type == typeof(short) || type == typeof(byte))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return CellValues.Number;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return CellValues.Number;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else if (type == typeof(DateTime))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return CellValues.Date;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else if (type == typeof(bool))&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return CellValues.Boolean;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return CellValues.String;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;private static string GetCellValueAsString(object value, CellValues cellType)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (value == DBNull.Value)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return string.Empty;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; switch (cellType)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; case CellValues.Boolean:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return (bool)value ? &quot;1&quot; : &quot;0&quot;;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; case CellValues.Date:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; DateTime dateValue = (DateTime)value;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Excel stores dates as OLE Automation dates&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return dateValue.ToOADate().ToString(CultureInfo.InvariantCulture);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; case CellValues.Number:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return Convert.ToString(value, CultureInfo.InvariantCulture);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; default:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return Convert.ToString(value);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/2921108624931558793/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2025/12/openxml-csv-excel.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/2921108624931558793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/2921108624931558793'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2025/12/openxml-csv-excel.html' title='[點網]使用 openxml 從 CSV 檔轉成 excel 檔'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-6013029173658165714</id><published>2025-11-14T00:12:00.000-08:00</published><updated>2025-11-14T00:12:28.890-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="git"/><title type='text'>[git]git 推到別站</title><content type='html'>&lt;p&gt;&amp;nbsp;# 假設你已經從 A 站 clone 下來，這時 origin 指向 A 站&lt;/p&gt;&lt;p&gt;git remote rename origin upstream&lt;/p&gt;&lt;p&gt;# 新增 B 站的遠端儲存庫&lt;/p&gt;&lt;p&gt;git remote add origin [B 站的 Repository URL]&lt;/p&gt;&lt;p&gt;# 之後你就可以 push 到 B 站了&lt;/p&gt;&lt;p&gt;git push -u origin main&lt;/p&gt;&lt;p&gt;# 如果需要，也可以從 A 站拉取更新&lt;/p&gt;&lt;p&gt;git pull upstream main&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/6013029173658165714/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2025/11/gitgit.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6013029173658165714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6013029173658165714'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2025/11/gitgit.html' title='[git]git 推到別站'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-4272300658804141637</id><published>2025-09-23T19:39:00.000-07:00</published><updated>2025-09-23T19:39:00.738-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="windows"/><title type='text'>[windows]設定 IP</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;這幾年，出門去測機，每每要改別人 windows 電腦的 IP，都找不到熟悉的介面改。&lt;/span&gt;&lt;/p&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;一個改 IP 的介面是越藏越深，越來越難找。&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;乾脆，用命令列來改反而一行解決。當然，首先，要開啟的是系統管理員權限的命令列&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;改成固定 IP 的命令如下:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;netsh interface ipv4 set address name=&quot;eth0&quot; static &lt;/span&gt;&lt;a class=&quot;x1fey0fg xmper1u x1edh9d7&quot; href=&quot;https://192.168.1.5&quot;&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;192.168.1.5&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt; &lt;/span&gt;&lt;a class=&quot;x1fey0fg xmper1u x1edh9d7&quot; href=&quot;https://255.255.255.0&quot;&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;255.255.255.0&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;改成 DHCP 的命令如下:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;netsh interface ipv4 set address name=&quot;eth0&quot; source=dhcp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;---&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;1. 那個 name 的值用 ipconfig 查&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;2. 為何不從 左下角窗戶&amp;gt;齒輪&amp;gt;網路與網際網路&amp;gt;乙太網路 改呢？因為改了沒生效。&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;3. 從 左下角窗戶&amp;gt;齒輪&amp;gt;網路與網際網路&amp;gt;進階網路設定 這裡有介面卡的名字，也可以改成方便記憶或下指令的名字&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;white-space-collapse: preserve;&quot;&gt;4. 舊的 IP 設定介面，從 左下角窗戶&amp;gt;齒輪&amp;gt;網路與網際網路&amp;gt;進階網路設定 點你要的介面卡，資訊會撐開，按下「編輯」按鈕。會打開舊版的設定畫面，在這裡設定 IPv4 的值，會直接生效。&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/4272300658804141637/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2025/09/windows-ip.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/4272300658804141637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/4272300658804141637'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2025/09/windows-ip.html' title='[windows]設定 IP'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-6499365317477467864</id><published>2024-11-11T18:46:00.000-08:00</published><updated>2024-11-11T18:46:41.441-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="c#"/><category scheme="http://www.blogger.com/atom/ns#" term="點網"/><title type='text'>[csharp]發生 Managed Debugging Assistant &#39;NonComVisibleBaseClass&#39; 錯誤</title><content type='html'>&lt;p&gt;&amp;nbsp;我不知道原因，但只知道解法。從解法來看，是 VS IDE 管太多卡到舊DLL了。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In Visual Studio 2019:&amp;nbsp;&lt;/p&gt;&lt;p&gt;Debug Menu, Windows --&amp;gt; Exception settings, opens the Exception settings window.&amp;nbsp;&lt;/p&gt;&lt;p&gt;There expand &quot;Managed Debugging Assistants&quot; and finally uncheck NonComVisibleBaseClass&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;參考:&lt;/p&gt;&lt;p&gt;https://stackoverflow.com/questions/1049742/noncomvisiblebaseclass-was-detected-how-do-i-fix-this&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/6499365317477467864/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2024/11/csharp-managed-debugging-assistant.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6499365317477467864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6499365317477467864'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2024/11/csharp-managed-debugging-assistant.html' title='[csharp]發生 Managed Debugging Assistant &#39;NonComVisibleBaseClass&#39; 錯誤'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-5271337741420211164</id><published>2024-08-27T00:13:00.000-07:00</published><updated>2024-08-27T00:14:34.719-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><title type='text'>[golang]http.HandleFunc 新的 routing pattern</title><content type='html'>&lt;p&gt;&amp;nbsp;在 Go1.22 的時候，讓 http.HandleFunc 新的 routing pattern 可以用變數的形式拿到 URL 路徑裡的值&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;要使用這個功能，要讓 go build 使用新的編譯方法，不然它總是用舊方法編譯，這是為了相容性。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;最簡單就是在 go.mod 裡加上一行 go 1.22 或是 go 1.23&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://stackoverflow.com/questions/28745161/with-gos-webserver-where-does-the-root-of-the-website-map-onto-the-filesystem&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://tip.golang.org/doc/godebug&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://programmingpercy.tech/blog/exciting-go-update-v-1-22/&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://github.com/babafemi99/up-I-go/blob/main/main.go&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://gowithore.hashnode.dev/go-up-or-go-down&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://stackoverflow.com/questions/24116147/how-to-download-file-in-browser-from-go-server&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://mileslin.github.io/2020/03/Golang/%E5%BB%BA%E7%AB%8B%E4%B8%8B%E8%BC%89%E6%AA%94%E6%A1%88%E7%9A%84-Http-Response/&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/5271337741420211164/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2024/08/golanghttphandlefunc-pattern.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/5271337741420211164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/5271337741420211164'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2024/08/golanghttphandlefunc-pattern.html' title='[golang]http.HandleFunc 新的 routing pattern'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-8559352265425744042</id><published>2024-03-18T19:45:00.000-07:00</published><updated>2024-03-18T19:45:49.664-07:00</updated><title type='text'>[secs]HSMS timeout</title><content type='html'>&lt;p&gt;&amp;nbsp;T6: Active 端&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; select.req &amp;lt;-T6-&amp;gt; select.rsp&lt;/p&gt;&lt;p&gt;T7: Passive 端&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; open -&amp;gt; not_select &amp;lt;-T7-&amp;gt; select&lt;/p&gt;&lt;p&gt;T5: Active 端&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; connect_fail &amp;lt;-T5-&amp;gt; connecting&lt;/p&gt;&lt;p&gt;T8: msg:{B &amp;lt;-T8-&amp;gt; B...}&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/8559352265425744042/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2024/03/secshsms-timeout.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8559352265425744042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8559352265425744042'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2024/03/secshsms-timeout.html' title='[secs]HSMS timeout'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-5417102870075472602</id><published>2024-03-07T18:51:00.000-08:00</published><updated>2024-03-07T18:51:34.458-08:00</updated><title type='text'>[csharp]C# 如何得知某個 class 有實作某個介面</title><content type='html'>&lt;p&gt;&amp;nbsp;## 方法&lt;/p&gt;&lt;p&gt;1. objectType.GetInterfaces().Contains(interfaceType)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2. interfaceType.IsAssignableFrom(objectType)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3. objectType.IsAssignableTo(interfaceType)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4. objectType.GetInterface(nameof(interface)) != null&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 參考來源&lt;/p&gt;&lt;p&gt;https://www.facebook.com/groups/1706638306295947/?multi_permalinks=3338835626409532&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/5417102870075472602/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2024/03/csharpc-class.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/5417102870075472602'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/5417102870075472602'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2024/03/csharpc-class.html' title='[csharp]C# 如何得知某個 class 有實作某個介面'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-8508205614287751204</id><published>2024-01-07T17:34:00.000-08:00</published><updated>2024-01-07T17:34:53.101-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="vmware"/><title type='text'>[vmware]突然出現 misc.rsba_no 不存在的問題</title><content type='html'>&lt;p&gt;&amp;nbsp;是否出現 misc.rsba_no 不存在的問題？其實解答不是表面上可以看到的。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我最近突然遇到很多個 suspend 的 vm 開起來就出現這個問題，導致許多暫存的東西皆無法回復。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;回頭去查發現這情況都發生在主機的 hyper-v 功能打開之後，來回測試一番，覺得應該是 CPU 特性有改變，讓 vmware 無法回復狀態。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;很重要的資料在 vm 裡沒存的話，還是改回去做處理。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/8508205614287751204/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2024/01/vmware-miscrsbano.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8508205614287751204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8508205614287751204'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2024/01/vmware-miscrsbano.html' title='[vmware]突然出現 misc.rsba_no 不存在的問題'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-8636553941742494640</id><published>2023-12-19T21:05:00.000-08:00</published><updated>2023-12-19T21:05:45.340-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="c#"/><title type='text'>[C#]C# 最近出現的 ? ! 是做什麼用的？</title><content type='html'>&lt;p&gt;&amp;nbsp;C# 最近出現的 ? ! 是做什麼用的？&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;最近終於找到一個圖快速介紹這些?!是做什麼用的。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;主要是用以解決 null 相關的問題的語法糖&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://twitter.com/mwaseemzakir/status/1647856976477450240/photo/1&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Ternary Operator (?:)&lt;/p&gt;&lt;p&gt;Null Forgiving Operator (!.)&lt;/p&gt;&lt;p&gt;Null Conditional Operator (?.)&lt;/p&gt;&lt;p&gt;Null Coalescing Operator (??)&lt;/p&gt;&lt;p&gt;Null Coalescing Assignment Operator (??=)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/8636553941742494640/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2023/12/cc.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8636553941742494640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8636553941742494640'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2023/12/cc.html' title='[C#]C# 最近出現的 ? ! 是做什麼用的？'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-2914875966160091408</id><published>2022-12-14T16:59:00.002-08:00</published><updated>2022-12-14T16:59:13.518-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dds"/><title type='text'>[DDS]Vortex Opensplice 建議跳槽到 Cyclone DDS</title><content type='html'>&lt;p&gt;&amp;gt; We, therefore, encourage the Vortex Opensplice open source community users to consider migrating to Cyclone DDS.&lt;/p&gt;&lt;p&gt;看到就準備要跳了喔&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://github.com/ADLINK-IST/opensplice&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;blockquote style=&quot;border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;&quot;&gt;&lt;p&gt;For more than a decade, the Vortex Opensplice open source project helped to evangelise and successfully deploy the OMG Data Distribution Service Technology in thousands of industrial and academic projects worldwide. The Vortex Opensplice core team and the community have gained tremendous experience and know-how from these interactions.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Capitalizing on our lengthy experience developing data-centric middleware in real-time distributed systems the core team launched Cyclone DDS, a brand-new open source OMG DDS implementation. Cyclone DDS under the Eclipse foundation governance continues our mission to fuel innovation and serve a more diverse customer base. It is gaining momentum in many opensource frameworks and industrial contexts including ROS2 , Autoware etc. It also represents a genuinely open-source data distribution solution with full source code access and updates.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Buoyed by this success and adoptions, it became obvious that the focus of the core team and the community should now be Cyclone DDS. We, therefore, encourage the Vortex Opensplice open source community users to consider migrating to Cyclone DDS. The migration is straightforward when the ISO CPP V2 APIs are in use. For users that are building mission- and/or business-critical systems, ADLINK continues to support Vortex Opensplice Professional Edition and offers a commercially supported version with extra features and guarantees of support.&lt;/p&gt;&lt;/blockquote&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/2914875966160091408/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2022/12/ddsvortex-opensplice-cyclone-dds.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/2914875966160091408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/2914875966160091408'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2022/12/ddsvortex-opensplice-cyclone-dds.html' title='[DDS]Vortex Opensplice 建議跳槽到 Cyclone DDS'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-8140852677579253893</id><published>2022-10-30T23:19:00.004-07:00</published><updated>2022-12-01T21:44:26.447-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="arduino"/><title type='text'>[arduino]Arduino IDE 2.0 強制設定為英文介面</title><content type='html'>&lt;p&gt;&amp;nbsp;Arduino IDE 1.x 是以 Java 為基底。而 Arduino IDE 2.0 則是以 Javascript 為基底。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;仔細的說，Arduino IDE 2.0 是以 node.js(後端) + Chromium(前端) 建構的 electron 架構為底。同樣使用 electron 為架構的有 vscode。(歷史的眼淚，開始是 github 用來做 atom 編輯器，在2022年6月8號，GitHub正式宣布在2022年12月15日關閉Atom，並存檔其儲存庫[by wiki]。)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;好處是，近年來的 js 前端風潮，投入的人真的比較眾多且新鮮。進展也許真的會快很多，UI/UX 是真的不一樣。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;現在對於 zh-tw 的翻譯還沒有完善，在我的電腦上，有些地方會使用到 zh-cn。如果像我這麼龜毛要處理的話，暴力處理就是強制使用 en，有兩個地方要處理。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;在 menu 上的中文要變英文，超暴力方法，執行檔目錄裡的 locales 目錄，底下除了 en-US.pak 之外的都殺掉。&lt;/li&gt;&lt;li&gt;在 output 裡的 message 不要看到中文，在 C:\Users\&amp;lt;User&amp;gt;\.arduinoIDE 底下的 arduino-cli.yaml 內容第一行，加上 locale: en &amp;lt;換行&amp;gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;這樣就可以全部都變英文。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/8140852677579253893/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2022/10/arduinoarduino-ide-20.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8140852677579253893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8140852677579253893'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2022/10/arduinoarduino-ide-20.html' title='[arduino]Arduino IDE 2.0 強制設定為英文介面'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-7428403590779008168</id><published>2022-10-07T23:05:00.001-07:00</published><updated>2022-10-07T23:05:22.038-07:00</updated><title type='text'>[小知識]從 arduino 學到 NTP packet</title><content type='html'>&lt;p&gt;&amp;nbsp;NTP 是網路對時的協定，在一般因為是作業系統自動處理，所以也不知到哪裡找資訊。&lt;/p&gt;&lt;p&gt;反而因為 arduino 這類 MCU 興起的關係，很多老協定的原始碼與 SPEC 就有了關鍵字可以深探與學習。&lt;/p&gt;&lt;p&gt;重點&lt;/p&gt;&lt;p&gt;* UDP 傳送，48 BYTE，UTC時間&lt;/p&gt;&lt;p&gt;兩段重點程式&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;unsigned long sendNTPpacket(IPAddress&amp;amp; address) {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; Serial.println(&quot;sending NTP packet...&quot;);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; // set all bytes in the buffer to 0&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; memset(packetBuffer, 0, NTP_PACKET_SIZE);&amp;nbsp; //clear the buffer&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; //Initialize values needed to form NTP request&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; //(see URL above for details on the packets)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[0]=0b11100011;&amp;nbsp; &amp;nbsp;// LI, Version, Mode&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[1]=0;&amp;nbsp; &amp;nbsp; &amp;nbsp;// Stratum, or type of clock&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[2]=6;&amp;nbsp; &amp;nbsp; &amp;nbsp;// Polling Interval&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[3]=0xEC;&amp;nbsp; // Peer Clock Precision&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; //8 bytes of zero for Root Delay &amp;amp; Root Dispersion&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[12]=49;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[13]=0x4E;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[14]=49;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; packetBuffer[15]=52;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; // all NTP fields have been given values, now&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; // you can send a packet requesting a timestamp:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; udp.beginPacket(address, 123); //NTP requests are to port 123&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; udp.write(packetBuffer, NTP_PACKET_SIZE); //send UDP request to NTP server&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; udp.endPacket();&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;unsigned long getUnixTime() {&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; WiFi.hostByName(ntpServerName, timeServerIP);&amp;nbsp; //get a random server from the pool&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; sendNTPpacket(timeServerIP);&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//send an NTP packet to a time server&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; delay(1000);&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// wait to see if a reply is available&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; int cb=udp.parsePacket();&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //return bytes received&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; unsigned long unix_time=0;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; if (!cb) {Serial.println(&quot;no packet yet&quot;);}&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; else {&amp;nbsp; //received a packet, read the data from the buffer&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.print(&quot;packet received, length=&quot;);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.println(cb);&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //=48&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; udp.read(packetBuffer, NTP_PACKET_SIZE);&amp;nbsp; //read the packet into the buffer&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //the timestamp starts at byte 40 of the received packet and is four bytes,&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //or two words, long. First, esxtract the two words:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long highWord=word(packetBuffer[40], packetBuffer[41]);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long lowWord=word(packetBuffer[42], packetBuffer[43]);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //combine the four bytes (two words) into a long integer&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //this is NTP time (seconds since Jan 1 1900):&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long secsSince1900=highWord &amp;lt;&amp;lt; 16 | lowWord;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.print(&quot;Seconds since Jan 1 1900=&quot; );&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.println(secsSince1900);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.print(&quot;Unix time=&quot;);&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Unix time starts on Jan 1 1970. In seconds, that&#39;s 2208988800:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unix_time=secsSince1900 - 2208988800UL;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.print(F(&quot;Unix time stamp (seconds since 1970-01-01)=&quot;));&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.println(unix_time); //print Unix time&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; return unix_time; //return seconds since 1970-01-01&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;參考&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;* [Arduino C on ESP8266 學習筆記 (三) : 從 NTP 伺服器取得網路時間](http://yhhuang1966.blogspot.com/2017/09/arduino-ide-esp8266-ntp.html)&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/7428403590779008168/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2022/10/arduino-ntp-packet.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/7428403590779008168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/7428403590779008168'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2022/10/arduino-ntp-packet.html' title='[小知識]從 arduino 學到 NTP packet'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-477821388459755179</id><published>2022-06-15T03:14:00.001-07:00</published><updated>2022-06-15T03:14:25.452-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="點網"/><title type='text'>[MSMQ]remote queue receive 遠端佇列接收</title><content type='html'>&lt;p&gt;&amp;nbsp;快速說結論&lt;/p&gt;&lt;p&gt;在 Queue Server 端的設定 (Windows 10, Windows Server 2012R2 在 Workgroup 下測試通過)&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoaLSuleRJlSrBaaJm8UNU8-3RB55QnA50V5m1kiwc5NPIPRUI88nHUlQj_eW5Pzh1QU6e8hsEONr0zB3eTUiD3nsVhhK5MIrWvBpfe1tZ_se2fqZBT_WgZbTtc5TgRzypo81aLupgqCW1xwo-aqOHavV9-YHfgDf-sjzDZ4CQu1RBcr4wkbP9jaw9/s647/QSERVERSETTING.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;647&quot; data-original-width=&quot;581&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoaLSuleRJlSrBaaJm8UNU8-3RB55QnA50V5m1kiwc5NPIPRUI88nHUlQj_eW5Pzh1QU6e8hsEONr0zB3eTUiD3nsVhhK5MIrWvBpfe1tZ_se2fqZBT_WgZbTtc5TgRzypo81aLupgqCW1xwo-aqOHavV9-YHfgDf-sjzDZ4CQu1RBcr4wkbP9jaw9/s320/QSERVERSETTING.png&quot; width=&quot;287&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguI-C1NNr2MltoCWEYHVDpbrz1lVo4lSBT8T9o8SYp0hGEd8Sv5-KluOP4-JdjD5DFTFgauI2LGOdkrp3D-IUBS-fVTv8e-zLJ5JcBb8cyBdqUAzu7JFCEzT8qV5VNcWy_Yx-b1vMFs5Ig7zuonq-0Ys9RbsWzA2xRPgGGHv6FUI7Ddhjsx_EDw0yC/s647/QSERVERSETTING2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;647&quot; data-original-width=&quot;581&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguI-C1NNr2MltoCWEYHVDpbrz1lVo4lSBT8T9o8SYp0hGEd8Sv5-KluOP4-JdjD5DFTFgauI2LGOdkrp3D-IUBS-fVTv8e-zLJ5JcBb8cyBdqUAzu7JFCEzT8qV5VNcWy_Yx-b1vMFs5Ig7zuonq-0Ys9RbsWzA2xRPgGGHv6FUI7Ddhjsx_EDw0yC/s320/QSERVERSETTING2.png&quot; width=&quot;287&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;然後 Guset 帳號不要開，Guset 帳號不要開，Guset 帳號不要開。&lt;/p&gt;&lt;p&gt;防火牆要注意一下。&lt;/p&gt;&lt;p&gt;就這樣。&lt;/p&gt;&lt;p&gt;其他更詳細的過程，有空再補充。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;http://nthrbldyblg.blogspot.com/2017/02/msmq-between-two-computers.html&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://docs.microsoft.com/zh-tw/archive/blogs/johnbreakwell/understanding-how-msmq-security-blocks-rpc-traffic&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms699854(v=vs.85)?redirectedfrom=MSDN&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/477821388459755179/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2022/06/msmqremote-queue-receive.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/477821388459755179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/477821388459755179'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2022/06/msmqremote-queue-receive.html' title='[MSMQ]remote queue receive 遠端佇列接收'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoaLSuleRJlSrBaaJm8UNU8-3RB55QnA50V5m1kiwc5NPIPRUI88nHUlQj_eW5Pzh1QU6e8hsEONr0zB3eTUiD3nsVhhK5MIrWvBpfe1tZ_se2fqZBT_WgZbTtc5TgRzypo81aLupgqCW1xwo-aqOHavV9-YHfgDf-sjzDZ4CQu1RBcr4wkbP9jaw9/s72-c/QSERVERSETTING.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-527956088462313035</id><published>2022-02-22T19:06:00.000-08:00</published><updated>2022-02-22T19:06:16.577-08:00</updated><title type='text'>[powershell] 使用 Windows PowerShell ISE</title><content type='html'>&lt;p&gt;&amp;nbsp;# Windows PowerShell ISE&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在使用 Windows PowerShell ISE 遇到以下錯誤，簡單來說是因為權限的關係。&lt;/p&gt;&lt;p&gt;預設一般使用者無法執行 powershell script。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;因為這個系統上已停用指令碼執行，所以無法載入 C:\x\00StartDevPCVM.ps1 檔案。如需詳細資訊，請參閱 about_Execution_Policies，網址為 https:/go.microsoft.com/fwlink/?LinkID=135170。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; + CategoryInfo&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : SecurityError: (:) [], ParentContainsErrorRecordException&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; + FullyQualifiedErrorId : UnauthorizedAccess&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;兩個解決方法，&lt;/p&gt;&lt;p&gt;(1)用 administrator 身份開啟 PowerShell 或 PowerShell ISE 來執行&lt;/p&gt;&lt;p&gt;(2)設定執行權限&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;設定執行權限，也是會遇到權限問題，所以也有兩個方法，&lt;/p&gt;&lt;p&gt;(1)用 administrator 身份開啟 PowerShell 或 PowerShell ISE 來執行「設定執行權限」指令&lt;/p&gt;&lt;p&gt;(2)只設定 CurrentUser 的執行權限&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;如果是使用方法(1)的話，以下指令可用。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;`Set-ExecutionPolicy RemoteSigned`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;如果是在使用者的 powershell 執行上面指令會出現&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;PS C:\x&amp;gt; Set-ExecutionPolicy RemoteSigned&lt;/p&gt;&lt;p&gt;Set-ExecutionPolicy : 拒絕存取登錄機碼 &#39;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell&#39;。 若要變更預設 (Loca&lt;/p&gt;&lt;p&gt;lMachine) 領域的執行原則，請使用 [以系統管理員身分執行] 選項啟動 Windows PowerShell。若要變更目前使用者的執行原則，請執行 &quot;Set-ExecutionPolicy -Scope CurrentUser&quot;。&lt;/p&gt;&lt;p&gt;位於 線路:1 字元:1&lt;/p&gt;&lt;p&gt;+ Set-ExecutionPolicy RemoteSigned&lt;/p&gt;&lt;p&gt;+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; + CategoryInfo&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : PermissionDenied: (:) [Set-ExecutionPolicy], UnauthorizedAccessException&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;那就使用以下指令&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;`Set-ExecutionPolicy -S CurrentUser RemoteSigned`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 參考指令&lt;/p&gt;&lt;p&gt;Set-ExecutionPolicy&lt;/p&gt;&lt;p&gt;Get-ExecutionPolicy&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Get-ExecutionPolicy -List&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* https://docs.microsoft.com/zh-tw/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.2&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/527956088462313035/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2022/02/powershell-windows-powershell-ise.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/527956088462313035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/527956088462313035'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2022/02/powershell-windows-powershell-ise.html' title='[powershell] 使用 Windows PowerShell ISE'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-986362574144817595</id><published>2021-05-24T02:16:00.000-07:00</published><updated>2021-05-24T02:16:01.178-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="點網"/><title type='text'>[點網]小技巧 想讓編譯出來的二進位檔固定</title><content type='html'>&lt;p&gt;&amp;nbsp;在 source code 不變的情況下，使得 build binary 要一樣的話，dotnet 編譯參數可以用這個&lt;/p&gt;&lt;p&gt;deterministic = true&lt;/p&gt;&lt;p&gt;參考：&lt;/p&gt;&lt;p&gt;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/code-generation&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/986362574144817595/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2021/05/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/986362574144817595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/986362574144817595'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2021/05/blog-post.html' title='[點網]小技巧 想讓編譯出來的二進位檔固定'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-5440802062674080012</id><published>2020-12-22T00:25:00.002-08:00</published><updated>2020-12-22T00:25:32.631-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="機器學習"/><title type='text'> [超譯] pyimagesearch 的 人臉識別</title><content type='html'>&lt;p&gt;&amp;nbsp;# [超譯] pyimagesearch 的 人臉識別&lt;/p&gt;&lt;p&gt;來源：https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;原文作者有提供原始碼，索取需要購買訂閱專案。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 前言&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;今天這個貼文會使用到&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* opencv&lt;/p&gt;&lt;p&gt;* python&lt;/p&gt;&lt;p&gt;* deep learning&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;深度學習式的人臉識別有(1)高準確度(2)快速這兩個特點，可以用在靜態影像與動態影像。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 用 opencv, python, deep learning 做人臉識別&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在這個教程裡，你們會學到如何用 opencv, python, deep learning 做人臉識別。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們會簡單地討論深度學習式的人臉識別如何運作，包含 &quot;deep metric learning&quot; 的概念。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;接下來會安裝所需要的函式庫。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;最後會實作適用於靜態影像與動態影像的人臉識別。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;最後可發現，我們實作的人臉識別有即時處理的能力。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 了解深度學習式人臉識別&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;所以，深度學習與人臉識別是如何做到的？&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;秘密是 &quot;deep metric learning&quot; 的技術。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;如果你曾用過其他的深度學習的技術，一般的做法是：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* 接受一個影像&lt;/p&gt;&lt;p&gt;* 輸出一個 分類/標籤 給那個影像&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;然而，deep metric learning 不一樣。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;deep metric learning 會輸出一個實數的特徵向量。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;dlib 這個臉部識別網路，會輸出 128-d 的特徵向量(也就是一串數字有 128 個)，該特徵向量就是用來數量化臉部特徵。訓練這個網路使用名叫 triplets 的方式來達成：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* 找三張照片，A人有兩張，&lt;/p&gt;&lt;p&gt;* B人有一張，調整權重讓 B人之間的兩張照片的特徵向量比較近，A人與 B人之間的特徵向量比較遠。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;套用到實際例子，有三張照片，一張是 Chad Smith，兩張是 Will Ferrell。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們的網路會數量化這些臉，為每個臉建立出 128-d 的特徵向量( embedding、quantification)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;接下來，一般的想法是調整我們神經網路的權重，讓兩張 Will Ferrell 比較靠近，與 Chad Smith 比較遠。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們的人臉識別的網路架構是取自 ResNet-34，來自 [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385) 作者是 He et al.，但是層數比較少，filter 也減半。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;網路是由 [Davis King](https://www.pyimagesearch.com/2017/03/13/an-interview-with-davis-king-creator-of-the-dlib-toolkit/) 所訓練，他的資料集約有 3百萬張影像，在 [Labeled Faces in the Wild](http://vis-www.cs.umass.edu/lfw/) 相較於其他現代手法有達到 99.38% 的準確度。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Davis King ([dlib](http://dlib.net/)作者) 與 [Adam Geitgey](https://www.adamgeitgey.com/) ([face_recognition](https://github.com/ageitgey/face_recognition)作者，此模組我們待會會用到) 兩人有詳細文章說明深度學習式的人臉識別的作法。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* [High Quality Face Recognition with Deep Metric Learning](http://blog.dlib.net/2017/02/high-quality-face-recognition-with-deep.html) (Davis)&lt;/p&gt;&lt;p&gt;* [Modern Face Recognition with Deep Learning](https://medium.com/@ageitgey/machine-learning-is-fun-part-4-modern-face-recognition-with-deep-learning-c3cffc121d78) (Adam)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;非常推薦你們閱讀上述兩篇文章。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 安裝人臉識別函式庫&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;除了 python 與 opencv 之外，還需要兩個函式庫&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* [dlib](http://dlib.net/)&lt;/p&gt;&lt;p&gt;* [face_recognition](https://github.com/ageitgey/face_recognition)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;dlib 由 [Davis King](https://www.pyimagesearch.com/2017/03/13/an-interview-with-davis-king-creator-of-the-dlib-toolkit/) 維護，包含我們人臉識別工作所需要的 &quot;deep metric learning&quot; 的實作。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;face_recognition 由 [Adam Geitgey](https://www.adamgeitgey.com/) 所創，包裝了 dlib 的人臉識別的功能，讓它更方便使用。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我假設你已經裝了 opencv，如果沒有，我的文章 [OpenCV install tutorials](https://www.pyimagesearch.com/opencv-tutorials-resources-guides/) 有介紹。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;接下來，來安裝 dlib 與 face_recognition 吧。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;gt; 原文作者非常建議使用 `virtualenv` 加上 `virtualenvwrapper`，以免有 package 污染的問題。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 安裝 dlib&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;gt; 有可能需要安裝 cmake，這個也可以用 `pip install cmake` 安裝&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;gt; 現在新的安裝包會自動看環境內有沒有足夠的函式庫，若有就會自己編譯成支援 GPU 的版本。&lt;/p&gt;&lt;p&gt;&amp;gt; Nvidia GPU 需要的有 CUDA Development Tools 與 cuDNN Library(這個要註冊 nvidia 開發者帳號，只要 email 即可申請)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;使用 pip 安裝&lt;/p&gt;&lt;p&gt;`pip install dlib`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;結束 (時代進步真方便)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 安裝 face_recognition&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;使用 pip 安裝&lt;/p&gt;&lt;p&gt;`pip install face_recogntition`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;結束&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 安裝 imutils&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;[imutils](https://github.com/jrosebr1/imutils)這個是方便包，一些 opencv 的組合招式都打包成函式供人取用，原文作者推薦。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;使用 pip 安裝&lt;/p&gt;&lt;p&gt;`pip install imutils`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 我們的人臉識別資料集&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;因為 Jurassic Park (1993) 是我最喜愛的電影，為了致敬 Jurassic World: Fallen Kingdom (2018) 在美國上映，我們將人臉識別用在這電影的幾個角色上：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* Alan Grant&lt;/p&gt;&lt;p&gt;* Claire Dearing&lt;/p&gt;&lt;p&gt;* Ellie Sattler&lt;/p&gt;&lt;p&gt;* Ian Malcolm&lt;/p&gt;&lt;p&gt;* John Hammond&lt;/p&gt;&lt;p&gt;* Owen Grady&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;資料集可以在 30 分鐘內使用我的方法建構。參閱 [How to (quickly) build a deep learning image dataset](https://pyimagesearch.com/2018/04/09/how-to-quickly-build-a-deep-learning-image-dataset/)。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;有了這資料集，我們將會：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* 建立每個臉的 128-d 特徵向量&lt;/p&gt;&lt;p&gt;* 用這些特徵向量從靜態影像與動態影像中識別出角色們的臉&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 人臉識別專案架構&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;.&lt;/p&gt;&lt;p&gt;├── dataset&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── alan_grant [22 entries]&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── claire_dearing [53 entries]&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── ellie_sattler [31 entries]&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── ian_malcolm [41 entries]&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── john_hammond [36 entries]&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;└── owen_grady [35 entries]&lt;/p&gt;&lt;p&gt;├── examples&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── example_01.png&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;├── example_02.png&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;└── example_03.png&lt;/p&gt;&lt;p&gt;├── output&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;└── lunch_scene_output.avi&lt;/p&gt;&lt;p&gt;├── videos&lt;/p&gt;&lt;p&gt;│&amp;nbsp; &amp;nbsp;└── lunch_scene.mp4&lt;/p&gt;&lt;p&gt;├── search_bing_api.py&lt;/p&gt;&lt;p&gt;├── encode_faces.py&lt;/p&gt;&lt;p&gt;├── recognize_faces_image.py&lt;/p&gt;&lt;p&gt;├── recognize_faces_video.py&lt;/p&gt;&lt;p&gt;├── recognize_faces_video_file.py&lt;/p&gt;&lt;p&gt;└── encodings.pickle&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們專案有 4 個上層目錄：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* dataset/： 包含六個角色的臉的影像，依據名字放置&lt;/p&gt;&lt;p&gt;* examples/： 三個人臉影像，不在 dataset 裡，用來測試。&lt;/p&gt;&lt;p&gt;* output/： 這裡會存放處理後的人臉識別的動態影像&lt;/p&gt;&lt;p&gt;* videos/： 輸入動態影像會放在這裡。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們也有六個檔案放在根目錄：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;* search_bing_api.py： 第一步是建立 dataset，(原文作者已經寫好程式，直接執行即可)。要學如何使用 Bing API 建立資料集，參閱：[這貼文](https://pyimagesearch.com/2018/04/09/how-to-quickly-build-a-deep-learning-image-dataset/)&lt;/p&gt;&lt;p&gt;* encode_faces.py：用來將人臉編碼成特徵向量。&lt;/p&gt;&lt;p&gt;* recognize_faces_image.py：識別靜態影像中的人臉(依據你的資料集的人臉特徵向量)。&lt;/p&gt;&lt;p&gt;* recognize_faces_video.py：識別來自 webcam 的動態影像中的人臉，並輸出成動態影像。&lt;/p&gt;&lt;p&gt;* recognize_faces_video_file.py：識別來自硬碟的動態影像中的人臉，並輸出成動態影像。但今天不會討論這個，因為其骨架跟 video stream file 一樣。&lt;/p&gt;&lt;p&gt;* encodings.pickle：人臉識別編碼，由 encode_faces.py 處理你的資料集後產生，並序列化到硬碟之中。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在建立完資料集後，我們會使用 encode_faces.py 建立特徵向量。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 使用 opencv 與 深度學習 建立人臉特徵向量&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在我們識別人臉之前，我們首先需要將人臉編碼。這裡並沒有真的訓練識別的網路，而是使用 dlib 已經訓練好的模型。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們當然可以自己從頭開始訓練自己的模型，或調整已存在的模型。但在這個專案來說太超過了。從頭訓練需要許多的影像。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;然後，在分類時，我們可以使用簡單 k-NN 模型加上投票的方式做出人臉分類。其他傳統機器學習模型也有這樣用。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 建立臉部特徵模型，使用 encode_faces.py。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;# import the necessary packages&lt;/p&gt;&lt;p&gt;from imutils import paths&lt;/p&gt;&lt;p&gt;import face_recognition&lt;/p&gt;&lt;p&gt;import argparse&lt;/p&gt;&lt;p&gt;import pickle&lt;/p&gt;&lt;p&gt;import cv2&lt;/p&gt;&lt;p&gt;import os&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# construct the argument parser and parse the arguments&lt;/p&gt;&lt;p&gt;ap = argparse.ArgumentParser()&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-i&quot;, &quot;--dataset&quot;, required=True,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;path to input directory of faces + images&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-e&quot;, &quot;--encodings&quot;, required=True,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;path to serialized db of facial encodings&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-d&quot;, &quot;--detection-method&quot;, type=str, default=&quot;cnn&quot;,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;face detection model to use: either `hog` or `cnn`&quot;)&lt;/p&gt;&lt;p&gt;args = vars(ap.parse_args())&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# grab the paths to the input images in our dataset&lt;/p&gt;&lt;p&gt;print(&quot;[INFO] quantifying faces...&quot;)&lt;/p&gt;&lt;p&gt;imagePaths = list(paths.list_images(args[&quot;dataset&quot;]))&lt;/p&gt;&lt;p&gt;# initialize the list of known encodings and known names&lt;/p&gt;&lt;p&gt;knownEncodings = []&lt;/p&gt;&lt;p&gt;knownNames = []&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# loop over the image paths&lt;/p&gt;&lt;p&gt;for (i, imagePath) in enumerate(imagePaths):&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# extract the person name from the image path&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;print(&quot;[INFO] processing image {}/{}&quot;.format(i + 1,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;len(imagePaths)))&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;name = imagePath.split(os.path.sep)[-2]&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# load the input image and convert it from BGR (OpenCV ordering)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# to dlib ordering (RGB)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;image = cv2.imread(imagePath)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # detect the (x, y)-coordinates of the bounding boxes&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# corresponding to each face in the input image&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;boxes = face_recognition.face_locations(rgb,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;model=args[&quot;detection_method&quot;])&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# compute the facial embedding for the face&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;encodings = face_recognition.face_encodings(rgb, boxes)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# loop over the encodings&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;for encoding in encodings:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# add each encoding + name to our set of known names and&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# encodings&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;knownEncodings.append(encoding)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;knownNames.append(name)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# dump the facial encodings + names to disk&lt;/p&gt;&lt;p&gt;print(&quot;[INFO] serializing encodings...&quot;)&lt;/p&gt;&lt;p&gt;data = {&quot;encodings&quot;: knownEncodings, &quot;names&quot;: knownNames}&lt;/p&gt;&lt;p&gt;f = open(args[&quot;encodings&quot;], &quot;wb&quot;)&lt;/p&gt;&lt;p&gt;f.write(pickle.dumps(data))&lt;/p&gt;&lt;p&gt;f.close()&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 從靜態影像中識別出角色&lt;/p&gt;&lt;p&gt;recognize_faces_image.py&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;# import the necessary packages&lt;/p&gt;&lt;p&gt;import face_recognition&lt;/p&gt;&lt;p&gt;import argparse&lt;/p&gt;&lt;p&gt;import pickle&lt;/p&gt;&lt;p&gt;import cv2&lt;/p&gt;&lt;p&gt;# construct the argument parser and parse the arguments&lt;/p&gt;&lt;p&gt;ap = argparse.ArgumentParser()&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-e&quot;, &quot;--encodings&quot;, required=True,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;path to serialized db of facial encodings&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-i&quot;, &quot;--image&quot;, required=True,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;path to input image&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-d&quot;, &quot;--detection-method&quot;, type=str, default=&quot;cnn&quot;,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;face detection model to use: either `hog` or `cnn`&quot;)&lt;/p&gt;&lt;p&gt;args = vars(ap.parse_args())&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# load the known faces and embeddings&lt;/p&gt;&lt;p&gt;print(&quot;[INFO] loading encodings...&quot;)&lt;/p&gt;&lt;p&gt;data = pickle.loads(open(args[&quot;encodings&quot;], &quot;rb&quot;).read())&lt;/p&gt;&lt;p&gt;# load the input image and convert it from BGR to RGB&lt;/p&gt;&lt;p&gt;image = cv2.imread(args[&quot;image&quot;])&lt;/p&gt;&lt;p&gt;rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)&lt;/p&gt;&lt;p&gt;# detect the (x, y)-coordinates of the bounding boxes corresponding&lt;/p&gt;&lt;p&gt;# to each face in the input image, then compute the facial embeddings&lt;/p&gt;&lt;p&gt;# for each face&lt;/p&gt;&lt;p&gt;print(&quot;[INFO] recognizing faces...&quot;)&lt;/p&gt;&lt;p&gt;boxes = face_recognition.face_locations(rgb,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;model=args[&quot;detection_method&quot;])&lt;/p&gt;&lt;p&gt;encodings = face_recognition.face_encodings(rgb, boxes)&lt;/p&gt;&lt;p&gt;# initialize the list of names for each face detected&lt;/p&gt;&lt;p&gt;names = []&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# loop over the facial embeddings&lt;/p&gt;&lt;p&gt;for encoding in encodings:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# attempt to match each face in the input image to our known&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# encodings&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;matches = face_recognition.compare_faces(data[&quot;encodings&quot;],&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;encoding)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;name = &quot;Unknown&quot;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # check to see if we have found a match&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;if True in matches:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# find the indexes of all matched faces then initialize a&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# dictionary to count the total number of times each face&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# was matched&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;matchedIdxs = [i for (i, b) in enumerate(matches) if b]&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;counts = {}&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# loop over the matched indexes and maintain a count for&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# each recognized face face&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;for i in matchedIdxs:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;name = data[&quot;names&quot;][i]&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;counts[name] = counts.get(name, 0) + 1&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# determine the recognized face with the largest number of&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# votes (note: in the event of an unlikely tie Python will&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# select first entry in the dictionary)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;name = max(counts, key=counts.get)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# update the list of names&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;names.append(name)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# loop over the recognized faces&lt;/p&gt;&lt;p&gt;for ((top, right, bottom, left), name) in zip(boxes, names):&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# draw the predicted face name on the image&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;cv2.rectangle(image, (left, top), (right, bottom), (0, 255, 0), 2)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;y = top - 15 if top - 15 &amp;gt; 15 else top + 15&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;cv2.putText(image, name, (left, y), cv2.FONT_HERSHEY_SIMPLEX,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;0.75, (0, 255, 0), 2)&lt;/p&gt;&lt;p&gt;# show the output image&lt;/p&gt;&lt;p&gt;cv2.imshow(&quot;Image&quot;, image)&lt;/p&gt;&lt;p&gt;cv2.waitKey(0)&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 從 webcam 識別出角色&lt;/p&gt;&lt;p&gt;recognize_faces_video.py&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;# import the necessary packages&lt;/p&gt;&lt;p&gt;from imutils.video import VideoStream&lt;/p&gt;&lt;p&gt;import face_recognition&lt;/p&gt;&lt;p&gt;import argparse&lt;/p&gt;&lt;p&gt;import imutils&lt;/p&gt;&lt;p&gt;import pickle&lt;/p&gt;&lt;p&gt;import time&lt;/p&gt;&lt;p&gt;import cv2&lt;/p&gt;&lt;p&gt;# construct the argument parser and parse the arguments&lt;/p&gt;&lt;p&gt;ap = argparse.ArgumentParser()&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-e&quot;, &quot;--encodings&quot;, required=True,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;path to serialized db of facial encodings&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-o&quot;, &quot;--output&quot;, type=str,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;path to output video&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-y&quot;, &quot;--display&quot;, type=int, default=1,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;whether or not to display output frame to screen&quot;)&lt;/p&gt;&lt;p&gt;ap.add_argument(&quot;-d&quot;, &quot;--detection-method&quot;, type=str, default=&quot;cnn&quot;,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;help=&quot;face detection model to use: either `hog` or `cnn`&quot;)&lt;/p&gt;&lt;p&gt;args = vars(ap.parse_args())&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# load the known faces and embeddings&lt;/p&gt;&lt;p&gt;print(&quot;[INFO] loading encodings...&quot;)&lt;/p&gt;&lt;p&gt;data = pickle.loads(open(args[&quot;encodings&quot;], &quot;rb&quot;).read())&lt;/p&gt;&lt;p&gt;# initialize the video stream and pointer to output video file, then&lt;/p&gt;&lt;p&gt;# allow the camera sensor to warm up&lt;/p&gt;&lt;p&gt;print(&quot;[INFO] starting video stream...&quot;)&lt;/p&gt;&lt;p&gt;vs = VideoStream(src=0).start()&lt;/p&gt;&lt;p&gt;writer = None&lt;/p&gt;&lt;p&gt;time.sleep(2.0)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# loop over frames from the video file stream&lt;/p&gt;&lt;p&gt;while True:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# grab the frame from the threaded video stream&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;frame = vs.read()&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# convert the input frame from BGR to RGB then resize it to have&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# a width of 750px (to speedup processing)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;rgb = imutils.resize(frame, width=750)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;r = frame.shape[1] / float(rgb.shape[1])&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# detect the (x, y)-coordinates of the bounding boxes&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# corresponding to each face in the input frame, then compute&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# the facial embeddings for each face&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;boxes = face_recognition.face_locations(rgb,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;model=args[&quot;detection_method&quot;])&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;encodings = face_recognition.face_encodings(rgb, boxes)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;names = []&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # loop over the facial embeddings&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;for encoding in encodings:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# attempt to match each face in the input image to our known&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# encodings&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;matches = face_recognition.compare_faces(data[&quot;encodings&quot;],&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;encoding)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;name = &quot;Unknown&quot;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# check to see if we have found a match&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;if True in matches:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# find the indexes of all matched faces then initialize a&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# dictionary to count the total number of times each face&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# was matched&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;matchedIdxs = [i for (i, b) in enumerate(matches) if b]&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;counts = {}&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# loop over the matched indexes and maintain a count for&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# each recognized face face&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;for i in matchedIdxs:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;				&lt;/span&gt;name = data[&quot;names&quot;][i]&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;				&lt;/span&gt;counts[name] = counts.get(name, 0) + 1&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# determine the recognized face with the largest number&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# of votes (note: in the event of an unlikely tie Python&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;# will select first entry in the dictionary)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;name = max(counts, key=counts.get)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# update the list of names&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;names.append(name)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # loop over the recognized faces&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;for ((top, right, bottom, left), name) in zip(boxes, names):&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# rescale the face coordinates&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;top = int(top * r)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;right = int(right * r)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;bottom = int(bottom * r)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;left = int(left * r)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# draw the predicted face name on the image&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cv2.rectangle(frame, (left, top), (right, bottom),&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;(0, 255, 0), 2)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;y = top - 15 if top - 15 &amp;gt; 15 else top + 15&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cv2.putText(frame, name, (left, y), cv2.FONT_HERSHEY_SIMPLEX,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;0.75, (0, 255, 0), 2)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # if the video writer is None *AND* we are supposed to write&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# the output video to disk initialize the writer&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;if writer is None and args[&quot;output&quot;] is not None:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;fourcc = cv2.VideoWriter_fourcc(*&quot;MJPG&quot;)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;writer = cv2.VideoWriter(args[&quot;output&quot;], fourcc, 20,&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;(frame.shape[1], frame.shape[0]), True)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# if the writer is not None, write the frame with recognized&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# faces to disk&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;if writer is not None:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;writer.write(frame)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; # check to see if we are supposed to display the output frame to&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;# the screen&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;if args[&quot;display&quot;] &amp;gt; 0:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cv2.imshow(&quot;Frame&quot;, frame)&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;key = cv2.waitKey(1) &amp;amp; 0xFF&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;# if the `q` key was pressed, break from the loop&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;if key == ord(&quot;q&quot;):&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;break&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# do a bit of cleanup&lt;/p&gt;&lt;p&gt;cv2.destroyAllWindows()&lt;/p&gt;&lt;p&gt;vs.stop()&lt;/p&gt;&lt;p&gt;# check to see if the video writer point needs to be released&lt;/p&gt;&lt;p&gt;if writer is not None:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;writer.release()&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;### 從影像檔中識別出角色&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;先前提過，recognize_faces_video_file.py 基本上跟前一個程式一模一樣，差別只在影像來源是影像檔而不是 webcam。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 能否在樹莓派執行這些程式？&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;基本上可以，但是有些限制&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1. 樹莓派記憶體不夠使用 CNN-based 臉部偵測&lt;/p&gt;&lt;p&gt;2. 所以只能用 HOG 臉部偵測&lt;/p&gt;&lt;p&gt;3. HOG 在樹莓派上太慢，無法勝任即時臉部偵測&lt;/p&gt;&lt;p&gt;4. 所以需要使用 opencv haar cascades&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;(譯註：我的電腦 16G 也沒辦法做 CNN-based 臉部偵測)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在樹莓派上的速度約是 1-2 FPS。好消息時之後我會回來討論如何在樹莓派上執行這些程式，敬請期待。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 結論&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;在這教程，你們學到了如何使用 opencv, python, deep learning 執行人臉識別。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們利用了 Davis King 的 dlib 與 Adam Geitgey 的 face_recognition，讓實作更方便。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;我們也看到，這裡提出的程式在準確度與有 GPU 的情況下即時運算的能力皆有達到水準。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;希望你們喜歡這則人臉識別的貼文。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;To download the source code to this post, and be notified when future tutorials are published here on PyImageSearch, just enter your email address in the form below!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/5440802062674080012/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/12/pyimagesearch.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/5440802062674080012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/5440802062674080012'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/12/pyimagesearch.html' title=' [超譯] pyimagesearch 的 人臉識別'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-2134145659226729657</id><published>2020-12-17T17:47:00.004-08:00</published><updated>2020-12-17T18:06:13.567-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><title type='text'>[linux] 小工具 網路流量監控 nethogs 與 vnstat 與 iftop</title><content type='html'>&lt;p&gt;&amp;nbsp;vnstat 執行後會不停計算網路流量，在結束之後，會出現統計數字，結算執行開始到結束的總流量與流速。這個比較適合很多台的流量報表產生。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Monitoring eth0...&amp;nbsp; &amp;nbsp; (press CTRL-C to stop)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;rx:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;51 kbit/s&amp;nbsp; &amp;nbsp;105 p/s&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; tx:&amp;nbsp; &amp;nbsp; &amp;nbsp;6.64 Mbit/s&amp;nbsp; &amp;nbsp;357 p/s^C&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;eth0&amp;nbsp; /&amp;nbsp; traffic statistics&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;rx&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;|&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;tx&lt;/p&gt;&lt;p&gt;--------------------------------------+------------------&lt;/p&gt;&lt;p&gt;&amp;nbsp; bytes&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 435 KiB&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;44.99 MiB&lt;/p&gt;&lt;p&gt;--------------------------------------+------------------&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; max&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;106 kbit/s&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp;9.71 Mbit/s&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; average&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;68.49 kbit/s&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp;7.26 Mbit/s&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; min&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 42 kbit/s&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp;3.32 Mbit/s&lt;/p&gt;&lt;p&gt;--------------------------------------+------------------&lt;/p&gt;&lt;p&gt;&amp;nbsp; packets&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;7321&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;22559&lt;/p&gt;&lt;p&gt;--------------------------------------+------------------&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; max&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 218 p/s&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;600 p/s&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; average&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 140 p/s&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;433 p/s&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; min&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;84 p/s&amp;nbsp; |&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;208 p/s&lt;/p&gt;&lt;p&gt;--------------------------------------+------------------&lt;/p&gt;&lt;p&gt;&amp;nbsp; time&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 52 seconds&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;nethogs 依照系統 process 分別列出流量，這個會比較適合在一台機器內抓誰是異常。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;NetHogs version 0.8.5-2&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; PID USER&amp;nbsp; &amp;nbsp; &amp;nbsp;PROGRAM&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; DEV&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SENT&amp;nbsp; &amp;nbsp; &amp;nbsp; RECEIVED&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;6344 pi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;..sr/lib/vino/vino-server&amp;nbsp; eth0&amp;nbsp; &amp;nbsp; &amp;nbsp; 154.737&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1.080 KB/sec&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;iftop 依照對外部端點的進出列出流量，這個適合在一群機器裡可以抓出流量去哪兒了。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;參考&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;https://askubuntu.com/questions/2411/how-do-i-find-out-which-process-is-eating-up-my-bandwidth&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/2134145659226729657/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/12/linux-nethogs-vnstat.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/2134145659226729657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/2134145659226729657'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/12/linux-nethogs-vnstat.html' title='[linux] 小工具 網路流量監控 nethogs 與 vnstat 與 iftop'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-8996283840805182176</id><published>2020-12-17T17:35:00.000-08:00</published><updated>2020-12-17T17:36:44.542-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="記憶體"/><title type='text'>[python] pympler檢查記憶體使用量</title><content type='html'>一個可以檢查記憶體使用量的lib。&lt;br /&gt;
&lt;br /&gt;
https://pythonhosted.org/Pympler/&lt;br /&gt;
&lt;br /&gt;
from pympler import muppy&lt;br /&gt;
from pympler import summary&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&lt;br /&gt;
all_objects = muppy.get_objects()&lt;br /&gt;
all = summary.summarize(all_objects)&lt;br /&gt;
summary.print_(all)</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/8996283840805182176/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2018/01/python-pympler.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8996283840805182176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/8996283840805182176'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2018/01/python-pympler.html' title='[python] pympler檢查記憶體使用量'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-6709379992251088979</id><published>2020-11-18T00:55:00.003-08:00</published><updated>2020-11-18T01:03:51.710-08:00</updated><title type='text'>[NAS]檔案儲存的安全</title><content type='html'>&lt;div&gt;我很早之前是使用 mac 的 softraid，將兩個USB外接硬碟組成磁碟陣列，比起當時的其他的 raid 方案更彈性。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;當硬碟失效時，可以換個硬碟，拉進磁碟陣列中等重建過後，即可恢復上線。&lt;/div&gt;&lt;div&gt;當 mac 失效時，可以換個 mac，兩個硬碟插入開始，磁碟陣列就可上線。&lt;/div&gt;&lt;div&gt;對硬碟沒有什麼要求，對 mac 的新舊也沒有要求。壞什麼就換什麼。就算只剩一個硬碟是好的，也可以恢復，也可以直接讀單顆硬碟裡的資料。後來因為太好用，所以後來一個 mac 上就接到 6 個硬碟，有三組磁碟陣列。用 samba 服務提供其他電腦存取。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;但是時間久了，硬碟常斷電下線，手動回復重建時又可能遇到斷電下線，後來才知道有可能 USB 電源供應會因為年紀而衰退。但當時並不知道，所以重覆個幾次，就有資料毀損的情況產生。當把 mac 退役後，才感受到，一個 mac 掛三組磁碟陣列，一但失去它，就全部檔案無法使用的影響太大。但又沒找到更好方法，接下來幾年就只有使用單碟冷備份的方式存放檔案。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;後來知道有 glusterfs，可以多主機共組磁碟陣列，彈性也是很高，於是採用樹莓派加個外接硬碟組成一個節點，兩個節點組成一組磁碟陣列。如此，四個部件壞掉哪一個都不會影響正常檔案存取。就算只剩一顆硬碟，用任何 linux 也可以讀到原來的檔案，將來沒有 glusterfs 也不用怕資料無法回復。而且變更節點組成的時候，可以線上變更，在修理、維護的時候仍然可以提供服務。一開始仍是用 samba 分享，但是仍然會有多檔、大檔會複製失敗的情況，後來自己寫個非常簡單的上傳程式，也就用快三年。也經歷過幾次硬碟壞掉或樹莓派壞掉的情況，修起來因為沒有圖形介面比較需要動腦，但是真的是很放心。在本機端來看，一組磁碟陣列就是一個硬碟掛到一般目錄之下，所以可以直接透過檔案系統做更複雜的管理(新增、刪除、移動)。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最近，又得知有個 min.io 的分散式網路儲存庫，號稱是很無腦，也很有彈性，只是它的彈性是節點以上的彈性。節點本身被規定一但組成就不能被變更，每個節點可以不一樣但建議要一樣。但是群內加節點擴增容量或減節點維設都不會影響整個服務，且保證有一半的部件失效都可正常讀取，容量會是總磁碟的一半。因為其目標是針對網路儲存，所以有內建網頁服務與網路API，在網路應用上就不用我自己寫個程式做檔案上下傳，但它就沒有辦法用檔案系統直接管理檔案了而是直接使用它提供的指令或網頁服務。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;glusterfs 與 min.io 並不是互斥的選項，它們兩個也可以一起使用。glusterfs 是強在它的虛擬磁碟組成的動態彈性，而 min.io 是網路節點群的動態彈性。由這兩個的彈性可以組成自己想要的網路儲存系統。&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/6709379992251088979/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/11/nas.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6709379992251088979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/6709379992251088979'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/11/nas.html' title='[NAS]檔案儲存的安全'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-388199150240776762</id><published>2020-09-21T01:34:00.005-07:00</published><updated>2020-09-21T01:34:49.025-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><title type='text'>[gae]從 appcfg 移民到 gcloud</title><content type='html'>&lt;p&gt;&amp;nbsp;# 從 appcfg 移民到 gcloud&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Google App Engine 我用了很久，一開始它是使用 appcfg 這個指令來執行上傳網站內容到 GAE 的環境去。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;直到昨天才發現資料上傳的指令要改用 gcloud，而且，gcloud 的使用必須要綁定信用卡。以往，如果超額使用了，google 是想辦法限制你的 app，像我是使用免費額度的，用完就關閉，我也不在乎。但是現在的改變，是讓我非得要跟新用戶一樣，如果超額使用了，我自己要去關掉 app，不然我就會被追討錢。這一點很討厭啊。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;不過，我有信心我的小小網站沒有人看是不可能會超額的，所以還是硬著頭皮把信用卡綁了，接下來就是設定 app 自動上傳的步驟。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 移民的步驟&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;按照 https://cloud.google.com/appengine/docs/standard/python/sdk-gcloud-migration&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;下載 gcloud SDK，並且安裝它。&lt;/p&gt;&lt;p&gt;然後，到原本 app 的目錄上，執行 `gcloud app deploy &amp;lt;app 的目錄&amp;gt;`&lt;/p&gt;&lt;p&gt;然後照著指示做，像是登入、移除 app.yaml 裡的 application 與 version 改用指令指定之類的，都成功之後，上傳完畢會提醒你，要綁信用卡，否則 app build fail。所以再照著指示去綁信用卡。綁完就會成功上傳。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;如果像我要自動上傳，指令要多加 --quiet，這樣就不用互動了。另外，也指定 app 的 version，這樣就不會產生新的版本，應該會直接覆蓋。免得版本放太多沒清掉被收錢。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/388199150240776762/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/09/gae-appcfg-gcloud.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/388199150240776762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/388199150240776762'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/09/gae-appcfg-gcloud.html' title='[gae]從 appcfg 移民到 gcloud'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-1964208676810432334</id><published>2020-09-17T19:37:00.004-07:00</published><updated>2020-09-17T19:38:38.389-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="HP-UX"/><title type='text'>[HP-UX]延壽計畫 結束</title><content type='html'>&lt;p&gt;&amp;nbsp;HP-UX 老傢伙回家了，&lt;/p&gt;&lt;p&gt;客戶也沒問什麼。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;關於 Python 的事，簡單記錄一下。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;真心感謝這些 open source 的 source code 都有留著，&lt;/p&gt;&lt;p&gt;真的是夠老的程式，對於 hp-ux 還有一些修正，所以經過交叉試驗幾個版本的 python，&lt;/p&gt;&lt;p&gt;Python-2.3 應該是要修改比較少的。可以支援到網路與threading。&lt;/p&gt;&lt;p&gt;configure 的時候要使用 pth，而不要使用 pthread。&lt;/p&gt;&lt;p&gt;c編譯器要選 cc，c++編譯器要選 aCC。這兩個是老傢伙自己帶著的。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;因為不知道該怎麼處理 configure 要增加 cflags，&lt;/p&gt;&lt;p&gt;所以在產生 Makefile 之後，在 cflags 的地方要加上 +DA1.1&lt;/p&gt;&lt;p&gt;如果遇到 aCC 抱怨 ccpython.cc 看不懂或 link 時少掉 symbol，那就去改使用 python.c 的那一個。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;接下來就是球來就打，有抱怨就解的時間，大多就是修改重覆定義但內容不同的地方，或是去搜尋找不到的定義，都過了之就，就過了。報告完畢。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;後註：Python-2.6.9 要從 source 編譯，需要另一個 python，所以，它也有了雞生蛋的問題。這幾年遇到幾次翻新的工作，得到的感觸就是有空能翻新就翻新，不要等太久的話，自動工具就沒辦法自動了，這讓要翻新的工夫比弄新的還大。翻新需要的其實也都不難，就是做苦工，而苦工就是需要時間與耐心。這時代好像大家都缺乏時間與耐心。&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/1964208676810432334/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/09/hp-ux.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/1964208676810432334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/1964208676810432334'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/09/hp-ux.html' title='[HP-UX]延壽計畫 結束'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-3494066813053973554</id><published>2020-09-14T21:04:00.001-07:00</published><updated>2020-09-14T21:04:59.390-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><title type='text'>[golang]移植 go 到新的作業系統與架構</title><content type='html'>Go 1.5 之後，編譯 go 的方法&lt;br /&gt;https://github.com/embeddedgo/go/tree/embedded/src/cmd/dist&lt;br /&gt;https://docs.google.com/document/d/1OaatvGhEAq7VseQ9kkavxKNAfepWy2yhPUBs96FGV28/edit&lt;br /&gt;&lt;br /&gt;根據這個文件，安裝新的 go，需要已經編譯好的 go。在還沒有 go 的地方，只能從 go1.4 開始編譯。因為 go1.4 是最後一個用 c 產生的 go 的編譯器。更高的版本都必須要用 go 來編譯。&lt;br /&gt;&lt;br /&gt;官方文件 https://golang.org/doc/install/source 的方法，從 source 開始編譯 bootstrap toolchain，可以下載 https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz 。這個包裡面有做調整使它更適應新的作業系統。裡面可以支援的作業系統與指令集有限。&lt;br /&gt;&lt;br /&gt;在 cmd/dist/build.c 裡有列出可用的作業系統與指令集&lt;br /&gt;static char *okgoos[] = {&lt;br /&gt;&lt;blockquote style=&quot;border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;&quot;&gt;    &quot;darwin&quot;,&lt;br /&gt;    &quot;dragonfly&quot;,&lt;br /&gt;    &quot;linux&quot;,&lt;br /&gt;    &quot;android&quot;,&lt;br /&gt;    &quot;solaris&quot;,&lt;br /&gt;    &quot;freebsd&quot;,&lt;br /&gt;    &quot;nacl&quot;,&lt;br /&gt;    &quot;netbsd&quot;,&lt;br /&gt;    &quot;openbsd&quot;,&lt;br /&gt;    &quot;plan9&quot;,&lt;br /&gt;    &quot;windows&quot;,&lt;/blockquote&gt;};&lt;br /&gt;&lt;br /&gt;static char *gochars = &quot;5668&quot;;&lt;br /&gt;&lt;br /&gt;static char *okgoarch[] = {&lt;br /&gt;&lt;blockquote style=&quot;border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;&quot;&gt;    // same order as gochars&lt;br /&gt;    &quot;arm&quot;,&lt;br /&gt;    &quot;amd64&quot;,&lt;br /&gt;    &quot;amd64p32&quot;,&lt;br /&gt;    &quot;386&quot;,&lt;/blockquote&gt;};&lt;br /&gt;&lt;br /&gt;在裡面會用到的 assambler, compiler, go compiler, linker 的程式碼，就放在 cmd/dist/[5-8][a,c,g,l]&lt;br /&gt;例如，arm 架構就用 5[a,c,g,l]，386 架構就用 8[a,c,g,l]&lt;br /&gt;&lt;br /&gt;當 make.bash 能夠完整跑完，bootstrap toolchain 才算是完整。&lt;br /&gt;&lt;br /&gt;裡面的流程是&lt;br /&gt;1. 用 c 編譯 cmd/dist/dist 執行檔&lt;br /&gt;2. 用 cmd/dist/dist bootstrap -a 編譯出 go_bootstrap 執行檔(原始檔來源 cmd/go)。cmd/dist/dist 會利用 [5-8][c,l] 與 gcc 編譯 .c 檔，用 [5-8][a] 編譯 .s 檔，用 [5-8][g,l] 編譯 .go 檔。&lt;br /&gt;3. 用 go_bootstrap install 編譯 go 的其他 package，都是 .go 檔。&lt;br /&gt;4. 最後刪除 go_bootstrap 執行檔&lt;br /&gt;5. 用 cmd/dist/dist banner 顯示編譯資訊&lt;br /&gt;&lt;br /&gt;以下有詳細指令關於從 source 編譯與安裝 go&lt;br /&gt;&lt;br /&gt;http://www.boomerangsworld.de/cms/dev_guides/install_go.html&lt;br /&gt;&lt;br /&gt;從以上資訊，可以了解到，在新的作業系統與架構上安裝 go，可能有兩種途徑：&lt;br /&gt;&lt;br /&gt;1. 從 go1.4 開始，修正 cmd/dist/dist 的原始碼，認到正確的 GOOS GOARCH。然後在 cmd/dist/dist bootstrap 階段，提供正確的 a, c, g, l 的 .c, .s 檔案。&lt;br /&gt;&lt;br /&gt;2. 使用 cross compile 的方式，在已經可以正常執行的環境下，編譯新架構的 go。在 go 大於 1.5 之後的版本，go compiler 與 go linker 的程式架構已經改變，所以要修改的程式與原來的 cmd/dist/[5-8][a,c,g,l] 不一樣。在 goroot/src/cmd/internal/obj, cmd/asm/internal, src/cmd/link, src/cmd/compile/internal/ssa/gen 這些地方都要修正。https://embeddedgo.github.io/2019/11/19/porting_go_to_microcontrollers_part1.html https://embeddedgo.github.io/2019/11/24/porting_go_to_microcontrollers_part2.html&lt;br /&gt;&lt;br /&gt;假始只想要安裝 go1.4 的話，那麼就去修正 cmd/dist/[5-8][a,c,g,l] 應該就可以了。&lt;br /&gt;&lt;br /&gt;如果，想要移殖新版的 go，應該採用第二個方法。產生一個新的 go。&lt;br /&gt;如果採用第一個方法，在修正完 cmd/dist/[5-8][a,c,g,l] 之後，還要再修正 go compiler 與 go linker 的程式。等於做兩次工。&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/3494066813053973554/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/09/golang-go.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/3494066813053973554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/3494066813053973554'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/09/golang-go.html' title='[golang]移植 go 到新的作業系統與架構'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-4481909491096137693</id><published>2020-09-12T05:20:00.005-07:00</published><updated>2020-09-12T05:20:42.054-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="HP-UX"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>[HP-UX]HP-UX 延壽計畫</title><content type='html'>&lt;p&gt;&amp;nbsp;# HP-UX 延壽計畫&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;最近工作要與 HP-UX 溝通，要來了解一下它的環境。&lt;/p&gt;&lt;p&gt;查了一下網路，是個 unix 系統。It&#39;s a Unix system, I know this! https://www.youtube.com/watch?v=dFUlAQZB9Ng&lt;/p&gt;&lt;p&gt;(從 jerv 學來的梗)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 第一步&lt;/p&gt;&lt;p&gt;等到機器來了，先用鍵盤登入後，發現&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;只有 sh 與 vi&lt;/p&gt;&lt;p&gt;打錯字還不能 backspace ...&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;`vi` 簡單指令例如： i 遊標位置開始輸入、a 遊標位置後新增輸入、ESC 離開編輯模式、x 刪除遊標位置字元、dd 刪除遊標位置所在行、:w 存檔、:wq 存檔後離開。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;google 如何改網路設定，如下：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;設定 ip, mask, gateway&lt;/p&gt;&lt;p&gt;`vi /etc/rc.config.d/netconf`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;改變 IP_ADDRESS[0] SUBNET_MASK[0] ROUTE_GATEWAY[0]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;設定 ip 與 hostname 對應&lt;/p&gt;&lt;p&gt;`vi /etc/hosts`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;設定 dns server&lt;/p&gt;&lt;p&gt;`vi /etc/resolv.conf`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;然後，重開機，讓設定生效。(因為我不會手動使之生效)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 第二步&lt;/p&gt;&lt;p&gt;網路設定生效之後，就可以坐在位子上遠端了。不然機台離我位子很遠。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;首先看一下 uname&lt;/p&gt;&lt;p&gt;環境&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;mesf /# uname -a&lt;/p&gt;&lt;p&gt;HP-UX mesf B.10.20 A 9000/785 2010791808 two-user license&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;更改時間 使用 `date` 指令&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;hp-ux 的軟體管理工具程式有 `swlist` `swinstall` `swremove`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;已經內裝好的程式有：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 700QPK1020&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ACE.199912.02&amp;nbsp; Workstation Quality Pack for HP-UX 10.20 (December 1999)&lt;/p&gt;&lt;p&gt;&amp;nbsp; B3899BA&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;B.10.20.07&amp;nbsp; &amp;nbsp; &amp;nbsp;HP C/ANSI C Developer&#39;s Bundle for HP-UX 10.20 (S700)&lt;/p&gt;&lt;p&gt;&amp;nbsp; B3907CA&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;B.10.20.07&amp;nbsp; &amp;nbsp; &amp;nbsp;HP FORTRAN/S700 Compiler and its associated products&lt;/p&gt;&lt;p&gt;&amp;nbsp; B3907DB&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;B.10.20.14&amp;nbsp; &amp;nbsp; &amp;nbsp;HP Fortran 90/S700 Compiler and its associated products&lt;/p&gt;&lt;p&gt;&amp;nbsp; B3911DB&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;A.01.15.01&amp;nbsp; &amp;nbsp; &amp;nbsp;HP aC++ Compiler S700&lt;/p&gt;&lt;p&gt;&amp;nbsp; B5402AA_APZ&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;B.10.20&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; MirrorDisk/UX&lt;/p&gt;&lt;p&gt;&amp;nbsp; B6193EA&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;ACE.199912.01&amp;nbsp; Workstation ACE for HP-UX 10.20 (December 1999)&lt;/p&gt;&lt;p&gt;&amp;nbsp; B6378DA&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;ACE.199912.01&amp;nbsp; Networking ACE for HP-UX 10.20 (December 1999)&lt;/p&gt;&lt;p&gt;&amp;nbsp; HPUXEngCR700&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; B.10.20&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; English HP-UX CDE Runtime Environment&lt;/p&gt;&lt;p&gt;&amp;nbsp; Ignite-UX-10-20&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;A.2.3.205&amp;nbsp; &amp;nbsp; &amp;nbsp; HP-UX Installation Utilities for Installing 10.20 Systems&lt;/p&gt;&lt;p&gt;&amp;nbsp; J2559C&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; D.06.15&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Hewlett-Packard JetAdmin for Unix Utility&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;此外，ftp server 也已經可用，一開始我還用 ftp 指令下載檔案，痛苦。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;經過搜尋之後，以下這個網站的程式才可以用。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;http://mirrors.develooper.com/hpux/downloads.html&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;這個網站非常重要，只有它有 hp-ux 10.20 32bit PA-RISC 1.1 的工具程式。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;下載 `bash-4.3.30` `gcc-4.2.2 + binutils-2.16.1 + gdb-6.5`&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;依照網站說明&lt;/p&gt;&lt;p&gt;.sd.bz 用 bzip2 解開(`bzip2 -d foo-x.y.sd.bz`)，然後用 `swinstall -s &amp;lt;絕對路徑&amp;gt;/foo-x.y.sd` 安裝&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;其他想要用的程式，可能都要自己編譯安裝才行。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;## 第三步&lt;/p&gt;&lt;p&gt;目標是在這個老傢伙身上安裝軟體，如果不能的話，就自己寫，寫 fortran 當然是有點太遠，寫 c/c++ 來選我一定是選 c。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;但是，如果有其他的程式語言可以從這傢伙 build 出來，至少我可以不用傷腦筋字串、記憶體、指標、網路通訊的問題，時間一定會比較省，bug 應該也會比較少。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;一開始先找可用 gcc 編譯的程式語言，太新的語言大多相依 linux, 或新版的 gcc，或很新的 lib。最後可選的是 python，go。但是事情沒有這麼簡單，在嘗試過程中才知道，thread 這件事是要看系統與編譯系統，原來的系統與所附的的 cc 與 aCC 不支援 thread。我安裝的這個 gcc-4.2.2 + binutils-2.16.1 + gdb-6.5 也沒有支援。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;嘗試編譯 go 的過程非常複雜，最後就是卡到要寫 asm，放著。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;嘗試編譯 python 的過程非常複雜，有需要再說，總之現在得到一個堪用的 python，有些 module 沒有編成功。雖然沒有 thread，但是有網路功能。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;從 https://www.python.org/downloads/source/ 下載 Python-2.1.3.tgz，解開之後，進到 Python-2.1.3，&lt;/p&gt;&lt;p&gt;執行以下指令&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;./configure --without-threads&lt;/p&gt;&lt;p&gt;make&lt;/p&gt;&lt;p&gt;make test&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;結果是&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;108 tests OK.&lt;/p&gt;&lt;p&gt;3 tests failed: test_builtin test_pickle test_popen&lt;/p&gt;&lt;p&gt;30 tests skipped: test_al test_asynchat test_bsddb test_cd test_cl test_dl test_fcntl test_fork1 test_gdbm test_gl test_gzip test_imgfile test_largefile test_linuxaudiodev test_locale test_minidom test_nis test_openpty test_pty test_pyexpat test_sax test_socketserver test_sunaudiodev test_sundry test_thread test_threadedtempfile test_winreg test_winsound test_zipfile test_zlib&lt;/p&gt;&lt;p&gt;```&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;builtin 模組不能用有一點點讓人擔心，它的失敗原因是 `test test_builtin crashed -- exceptions.ValueError: int() literal too large: -2147483648` 這代表編譯時候的 int 與 python 認知 int 邊界可能有差異，可能所有的 int 都要小心。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;想要支援 thread，系統有安裝 pth，但是用 gcc 編譯有各種問題，之後再寫有哪些問題。&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/4481909491096137693/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2020/09/hp-uxhp-ux.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/4481909491096137693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/4481909491096137693'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2020/09/hp-uxhp-ux.html' title='[HP-UX]HP-UX 延壽計畫'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4257432075323421.post-773723733380506790</id><published>2019-10-01T00:39:00.001-07:00</published><updated>2019-10-01T00:39:18.447-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="c#"/><category scheme="http://www.blogger.com/atom/ns#" term="超譯"/><title type='text'>[超譯] 解決 .Net Reference 的版本衝突</title><content type='html'>## 前言&lt;br /&gt;
&lt;br /&gt;
最近遇到一個狀況，我包好的 dll 會用到 Newtonsoft.Json.dll (Json.Net)，版本是 4.5。拿去給別人(A公司)用，他們也用了別人(B公司)的 dll，也用到 Newtonsoft.Json.dll (Json.Net)，但是他們指定的是 9.0.0。當 Visual Studio 把我指定的 Newtonsoft.Json.dll 搬到執行檔旁邊，B公司的 dll 就無法啟動。當換成 B公司指定的，就換我的 dll 無法啟動。A公司的人大叫該怎麼辦？因為我們公司比較小，所以我要解決這個問題。&lt;br /&gt;
&lt;br /&gt;
## 主旨&lt;br /&gt;
&lt;br /&gt;
一般的解決方法都是叫大家重新抓過新版的 dll，像我們這次就是叫我這比較舊的版本要升級。但，支援的 .Net Framework 不能超過 4.5.2，超過會讓其他家的 dll 無法運作。這作法也沒什麼不好，只是沒有一勞永逸。要是將來我的 dll 給了幾家客戶，他們幾家要的 Newtonsoft.Json.dll 都不一樣，或是他們升級了，我這小公司就得配合找到對應的 Newtonsoft.Json.dll 來 rebuild 才行。&lt;br /&gt;
&lt;br /&gt;
網路上用不同關鍵字找過，我認為這個比較好。https://michaelscodingspot.com/how-to-resolve-net-reference-and-nuget-package-version-conflicts/&lt;br /&gt;
&lt;br /&gt;
## 超譯&lt;br /&gt;
&lt;br /&gt;
重點，解法有兩個方向，四個方法。&lt;br /&gt;
&lt;br /&gt;
兩個方向是 (1) 如果可以，全部改成同一個版本。 (2) 使用 dll side-by-side loading。&lt;br /&gt;
&lt;br /&gt;
四個解法之中，作者建議解法(1)&lt;br /&gt;
&lt;br /&gt;
### 解法(1) Use a single assembly version with Binding Redirect&lt;br /&gt;
&lt;br /&gt;
在有 app.config 或 dll.config 的存在時，增加一個 runtime 標籤。舉例：&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;br /&gt;
&amp;lt;configuration&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;lt;startup&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;supportedRuntime version=&quot;v4.0&quot; sku=&quot;.NETFramework,Version=v4.6.1&quot; /&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/startup&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;lt;runtime&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;dependentAssembly&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;assemblyIdentity name=&quot;log4net&quot; publicKeyToken=&quot;669e0ddf0bb1aa2a&quot; culture=&quot;neutral&quot; /&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;bindingRedirect oldVersion=&quot;0.0.0.0-5.0.0.0&quot; newVersion=&quot;1.2.11&quot; /&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/dependentAssembly&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/assemblyBinding&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;lt;/runtime&amp;gt;&lt;br /&gt;
&amp;lt;/configuration&amp;gt;&lt;br /&gt;
```&lt;br /&gt;
作者舉 log4net 為例，加上 runtime 標籤指示 CLR 使用 1.2.11 版的 log4net。&lt;br /&gt;
&lt;br /&gt;
### 解法(2) Override AssemblyResolve for side-by-side loading (No need for strong names)&lt;br /&gt;
&lt;br /&gt;
使用 AppDomain.CurrentDomain.AssemblyResolve 事件來處理不同版本的尋找問題。&lt;br /&gt;
&lt;br /&gt;
首先將要處理的 dll 的 CopyLocal 設成 False。並將不同版本的 dll 放到不同磁碟位置。然後在執行檔的最開頭註冊 AssemblyResolve 事件。它在 app 找不到 dll 的時候會被呼叫，然後我們就寫好程式來處理它。&lt;br /&gt;
&lt;br /&gt;
範例：&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
static void Main(string[] args)&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp; &amp;nbsp; AppDomain.CurrentDomain.AssemblyResolve += (sender, resolveArgs) =&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; string assemblyInfo = resolveArgs.Name;// e.g &quot;Lib1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var parts = assemblyInfo.Split(&#39;,&#39;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; string name = parts[0];&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var version = Version.Parse(parts[1].Split(&#39;=&#39;)[1]);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; string fullName;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (name == &quot;Lib1&quot; &amp;amp;&amp;amp; version.Major == 1 &amp;amp;&amp;amp; version.Minor == 0)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fullName = new FileInfo(@&quot;V10\Lib1.dll&quot;).FullName;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else if (name == &quot;Lib1&quot; &amp;amp;&amp;amp; version.Major == 1 &amp;amp;&amp;amp; version.Minor == 1)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fullName = new FileInfo(@&quot;V11\Lib1.dll&quot;).FullName;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return null;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return Assembly.LoadFile(fullName);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; };&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 解法(3) Copy assemblies to different folders and use &amp;lt;codebase&amp;gt; to load them side-by-side (Requires strong names)&lt;br /&gt;
&lt;br /&gt;
這個方法需要 dll 有強式名字。這點 Newtonsoft.Json.dll 或 log4net 這類公開的 dll 做得到。或是自己做的 dll 也來個強式名字也行。將要處理的 dll 的 CopyLocal 設成 False。&lt;br /&gt;
&lt;br /&gt;
舉例：&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;br /&gt;
&amp;lt;configuration&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;dependentAssembly&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;assemblyIdentity name=&quot;StrongNameLib&quot; culture=&quot;neutral&quot; publicKeyToken=&quot;6a770f8bdf3d476a&quot; /&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;codeBase version=&quot;1.0.0.0&quot; href=&quot;StrongNameLibV10/StrongNameLib.dll&quot;/&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;codeBase version=&quot;1.1.0.0&quot; href=&quot;StrongNameLibV11/StrongNameLib.dll&quot;/&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/dependentAssembly&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/assemblyBinding&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;lt;/runtime&amp;gt;&lt;br /&gt;
&amp;lt;/configuration&amp;gt;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
這就是告訴 CLR 按照版本去找 dll。如果沒有強式名字，CLR 會忽略版本，所以總是找到第一個 codeBase 所列的 dll。href 也可以用絕對路徑，像是 @&quot;FILE://c:/Lib/MyLib.dll&quot;&lt;br /&gt;
&lt;br /&gt;
### 解法(4) Install assemblies to the Global Assembly Cache (GAC)&lt;br /&gt;
&lt;br /&gt;
這個方法是把要用到的 dll 註冊到 GAC (Global Assembly Cache) 裡。這會需要 dll 有強式名字，而且還要使用者手動執行指令(或是用安裝程式)將 dll 註冊到 GAC 裡。例如：&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
gacutil.exe -i &quot;c:\Dev\Project\Debug\MyLib.dll&quot;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
將要處理的 dll 的 CopyLocal 設成 False。依照 runtime 的尋找規則，會先找尋 GAC 有沒有可用 dll，所以因為 GAC 都有註冊，所以各自會找到對應的版本來使用。&lt;br /&gt;
&lt;br /&gt;
這個方法會需要使用者手動註冊或者使用 installer 將 dll 註冊到 GAC。也就這樣而已。但是有些人就是會嫌……。</content><link rel='replies' type='application/atom+xml' href='http://codebeta.blogspot.com/feeds/773723733380506790/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://codebeta.blogspot.com/2019/10/net-reference.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/773723733380506790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4257432075323421/posts/default/773723733380506790'/><link rel='alternate' type='text/html' href='http://codebeta.blogspot.com/2019/10/net-reference.html' title='[超譯] 解決 .Net Reference 的版本衝突'/><author><name>beta</name><uri>http://www.blogger.com/profile/10209698907962040867</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>