変数名などの命名のお供に.Webサービス "Zenrei" を公開しました
Webサービス "Zenrei" を公開しました. GitHubのスター数上位のリポジトリから,クラス名・メソッド名・変数名の使用実績を検索できるサービスです.悩ましい命名のお供にご活用ください.
機能
- 使用実績 … GitHubスター数上位リポジトリから,入力したクラス名/メソッド名/変数名の使用頻度・使用箇所を表示
- 類語 … "calculate" - "estimate" のように,各単語の類語を表示
- betaにつきRubyのみ対応
活用例
"calculate" にするか "calc" にするか
"data" が複数形なので、 "datas" は使われていない
ソースコード
GitHubに公開しています.あまり綺麗ではないです.
技術要素
言語・フレームワーク等
- フロントエンド: Vue.js + Bootstrap
- サーバサイド: Go (Echo framework)
- データベース: MongoDB, SQLite3(日本語WordNetのみ)
- その他: Docker, Ruby(データ解析)
Vue.jsとGoは初でしたが,意外とすんなり出来ました.
クラス名/メソッド名/変数名の解析
ripper
ライブラリを使用しました.Ruby標準ライブラリで,ソースコードを与えるとS式にして返却してくれたりします.
類語の検索
日本語 WordNetを使用しました.SQLiteデータベースとして配布されており,synset(概念)というグループ化によって,類似する単語を探し当てることが出来ます.
なお,複数形・過去形などは原形(lemma)に戻して類語を調べています.これにはgolemライブラリを使用しました(GitHub: aaaton/golem).当初はwink-lemmatizerを用いてフロントエンド側で対応する予定でしたが,辞書サイズが大きくjsファイルが肥大化するためサーバサイドで対処しました.
細かいUI/UX
PKCS #1(RFC8017)のRSA鍵フォーマット 日本語訳
PKCS #1 / PKCS #8に準拠した,.NET向けRSA暗号鍵エンコーダ・デコーダライブラリをNuGetに公開しました.ソースコードや使い方はGitHubにあります.
この記事では,PKCS #1での鍵フォーマットについて説明します.
PKCS #1 (RFC8017)
RFC 8017 - PKCS #1: RSA Cryptography Specifications Version 2.2
公開鍵暗号標準であるPKCSのドキュメントのひとつ PKCS #1(RFC8017) は,RSA暗号の仕様を定めたものです.暗号化や署名のアルゴリズムはもちろんですが,鍵の表現方法についてもAppendixに記載があります.
鍵の表現方法を定めた部分を訳してみます.
RFC8017 Appendix A. ASN.1 Syntax
付録 A. ASN.1 構文 A.1. RSA鍵表現 この章では,RSA公開鍵・秘密鍵のASN.1オブジェクト識別子を定義するとともに, RSAPublicKey と RSAPrivateKey の型を定義します. 定義の対象用途には,X.509証明書,PKCS #8(RFC5958),PKCS #12(RFC7292)を含みます. オブジェクト識別子 rsaEncryption は, A.1.1 と A.1.2 で定義されるRSA公開鍵・秘密鍵を識別するものです.このオブジェクト識別子(OID)に関連付けられているAlgorithmIdentifier型のパラメタフィールドは,NULL型の値を持つものとします(SHALL). rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } この定義は,multi-prime RSAをサポートするよう拡張されていますが,過去のバージョンと下位互換性があります. A.1.1. RSA公開鍵構文 RSA公開鍵は,ASN.1のRSAPublicKey型により表現されます: RSAPublicKey ::= SEQUENCE { modulus INTEGER, -- n publicExponent INTEGER -- e } 各フィールドは次のような意味を持ちます: - modulus: RSA の modulus n - publicExponent: RSA の public exponent e A.1.2. RSA秘密鍵構文 RSA秘密鍵は,ASN.1のRSAPrivateKey型により表現されます: RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL } 各フィールドは次のような意味を持ちます: - version: 将来の改訂を考慮するためのバージョン番号.0とします(SHALL).ただし,multi-primeを使用する場合は1とします(SHALL). Version ::= INTEGER { two-prime(0), multi(1) } (CONSTRAINED BY {-- otherPrimesInfosを含む場合,versionは必ず multi(1) とすべきです --}) - modulus: RSA の modulus n - publicExponent: RSA の public exponent e - privateExponent: RSA の private exponent d - prime1: n の素因数 p - prime2: n の素因数 q - exponent1: d mod (p - 1) - exponent2: d mod (q - 1) - coefficient: CRT(中国剰余定理) の coefficient q^(-1) mod p - otherPrimeInfos: 追加の素数を r_3, ..., r_u, の順に含みます.version 0の場合は省略する(SHALL).version 1の場合は1つ以上のOtherPrimeInfoを含みます. OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo OtherPrimeInfo ::= SEQUENCE { prime INTEGER, -- ri exponent INTEGER, -- di coefficient INTEGER -- ti } OtherPrimeInfoのフィールドは次のような意味を持ちます: - prime: n の素因数 r_i (i >= 3) - exponent: d_i = d mod (r_i - 1) - coefficient: CRT(中国剰余定理)の coefficient t_i = (r_1 * r_2 * ... * r_(i-1))^(-1) mod r_i 注意: RSA秘密鍵は,流出や改ざんからの保護が重要です.保護方法についてはこの文書の範囲外です.秘密鍵やその他の暗号データを保存・配布する方法については,PKCS #12やPKCS #15で記述しています.
読み解くポイント: RSA, ASN.1, CRT
定義は明確ですが,いくつか前提知識を必要とします.
- modulus n, publicExponent eなどは,RSA暗号の鍵パラメタです.「パラメタだ」という理解で留めておくのが無難ですが,アルゴリズムを理解したい方は"サルにも分かる"から読み始めると良いかもしれません.
- SEQUENCE, INTEGER ほか,特殊な構文っぽい表記は ASN.1 という記法で記述されたものです.記法そのものは環境に依存しません.これを符号化(データ化)するアルゴリズムとして DER などが存在します.
- CRT(中国剰余定理)は,秘密鍵による復号(署名生成)を高速化するために使われています.こちらも「そのためのパラメタだ」という理解で留めておくのが無難ですが…
PKCS #1の鍵表現の使われどころ
-----BEGIN RSA PUBLIC KEY-----
で始まる文字列を見たことはあるでしょうか? あれは,PKCS #1形式によってRSA公開鍵が表現されています.
(※BEGIN PUBLIC KEY
で始まるのはPKCS #8形式です.そちらはまた別途.)
$ ssh-keygen -f .ssh/id_rsa.pub -e -m pem -----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAtH53M23IIXvXnM6d4nrZ9Q3BCU/HSMoYnZHlU6ytPXWZ5Y9oGAdX 4S2cMNOt7ZbiXXT9aRFyQwv80MCd6yMfrkHgFG0V2Oim59R1zypVXm7pvQmNApx3 Qvp+YMeEyLveFvn+wdwkeUSW1ag9D4ZIRwIE3kTqVxEj5BXElaaTW0HxYRDWVbrA MU9RSekhTq2bHRSKIfMcbqtu/tFj9SS/46XiCPd05wolorAYTY3y2ntPVc9hbepf IbxoiADm75pjxM1wg8ESOOmxSTQJO4hmdOEFRkMfRFFB65o2anN89Ubd1WJARwP5 +MYnliqPN3PO7eSqBw7qEzj+wqmRoX2YQwIDAQAB -----END RSA PUBLIC KEY-----
この公開鍵は,PKCS #1で定める RSAPublicKey を,DERとBASE64で符号化したものです.
これを,Asn1PKCSでデコードしてみましょう.
var key = "MIIBCgKCAQEAtH53M23IIXvXnM6d4nrZ9Q3BCU/HSMoYnZHlU6ytPXWZ5Y9oGAdX4S2cMNOt7ZbiXXT9aRFyQwv80MCd6yMfrkHgFG0V2Oim59R1zypVXm7pvQmNApx3Qvp+YMeEyLveFvn+wdwkeUSW1ag9D4ZIRwIE3kTqVxEj5BXElaaTW0HxYRDWVbrAMU9RSekhTq2bHRSKIfMcbqtu/tFj9SS/46XiCPd05wolorAYTY3y2ntPVc9hbepfIbxoiADm75pjxM1wg8ESOOmxSTQJO4hmdOEFRkMfRFFB65o2anN89Ubd1WJARwP5+MYnliqPN3PO7eSqBw7qEzj+wqmRoX2YQwIDAQAB"; var rsaParams = PKCS1DERDecoder.DecodePublicKey(key); Console.WriteLine("n(modulus) : 0x" + BitConverter.ToString(rsaParams.Modulus).Replace("-", string.Empty)); Console.WriteLine("e(publicExponent): 0x" + BitConverter.ToString(rsaParams.Exponent).Replace("-", string.Empty));
n(modulus) : 0xB47E77336DC8217BD79CCE9DE27AD9F50DC1094FC748CA189D91E553ACAD3D7599E58F68180757E12D9C30D3ADED96E25D74FD691172430BFCD0C09DEB231FAE41E0146D15D8E8A6E7D475CF2A555E6EE9BD098D029C7742FA7E60C784C8BBDE16F9FEC1DC24794496D5A83D0F8648470204DE44EA571123E415C495A6935B41F16110D655BAC0314F5149E9214EAD9B1D148A21F31C6EAB6EFED163F524BFE3A5E208F774E70A25A2B0184D8DF2DA7B4F55CF616DEA5F21BC688800E6EF9A63C4CD7083C11238E9B14934093B886674E10546431F445141EB9A366A737CF546DDD562404703F9F8C627962A8F3773CEEDE4AA070EEA1338FEC2A991A17D9843 e(publicExponent): 0x010001
できました.
Raspberry PiでNHKラジオ第一の緊急地震速報を検出する
P2P地震情報では,NHKラジオ第一の放送から緊急地震速報の発表を検出しています. 今回,その検出をPCではなくRaspberry Piで行うように変更しました.そのときの記録です.
前提: 自動音声による放送
NHKラジオ第一では,緊急地震速報(警報)が発表されると,通常の放送を中断して緊急地震速報の放送が入ります.
放送の内容は下記の通り.事前に収録した音声で自動的に放送されているようです.
(チャイム音)
緊急地震速報です.(震源名)で地震.次の地域は,強い揺れに警戒してください.(地域名)緊急地震速報です(以下繰り返しの後,該当地域の方々への注意喚起など)
「発表されたこと」だけを検出する場合,冒頭の音声をパターンマッチするだけで実現が出来ます.これをやります.
材料
- Raspberry Pi Zero W
- USB-オーディオ変換アダプタ( BSHSAU01BK : ヘッドセット | バッファロー)
- AMラジオ ( TY-SHR3:AM/FM/SWホームラジオ:東芝エルイートレーディング株式会社)
Raspberry Piによる録音
アダプタを接続するだけです.
$ lsusb Bus 001 Device 002: ID 0d8c:013c C-Media Electronics, Inc. CM108 Audio Controller Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub $ arecord -l **** ハードウェアデバイス CAPTURE のリスト **** カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio] サブデバイス: 1/1 サブデバイス #0: subdevice #0 $ arecord -L (省略) plughw:CARD=Device,DEV=0 USB PnP Sound Device, USB Audio Hardware device with all software conversions $ arecord -D plughw:1,0 test.wav 録音中 WAVE 'test.wav' : Unsigned 8 bit, レート 8000 Hz, モノラル
Python3による音声のパターンマッチ
GitHubにソース公開しています.見せるように作っていないのでかなり汚いです.
技術的なポイントはこのへん.
緊急地震速報の検出のためのポイントはこのへん.
- 検出負荷を下げるために正解データは短く
- チャイム音は2回繰り返すうち1回分
- 自動音声は冒頭の「緊急地」のみ
- 必ずチャイム音と音声をセットで検出(過去,チャイム音を用いた啓蒙放送等があったため)
※正解データは著作権の関係で公開できません.
実行
arecordの標準出力をPythonスクリプトの標準入力に食わせます.
$ arecord -D plughw:1,0 -t raw | python3 fft_analyze.py
過去の放送音声を流してみたところ,正しく認識されました.CPU使用率は約20%,良い感じです.
(失敗)pyaudioで録音&パターンマッチ
そもそもpyaudioライブラリを使えば,Pythonで完結させることが出来ます.しかし,失敗しました.
Input overflowed
の表示の通り,録音データをリアルタイムに処理出来ていません.
arecordでは8000Hz, 1ch, 8bitという低いデータレートで録音していましたが,これはarecord内部でリサンプリングされたものでした.一方,pyaudioにはそうした機能はありません.デバイスが対応する44100Hz, 1ch, 16bitという高いデータレートで録音するしかなく,リアルタイムに扱うには厳しい量でした.
その結果,arecordの標準出力から食わせる,という方法に落ち着きました.
参考
P2P地震情報14周年: 個人サービスを長く続けるための技術
個人開発 #2 Advent Calendar 2018 8日目の記事です.
せっかく作った個人サービスを長く続けていくための技術についてお話ししようと思います.
3行でまとめると
- Dogfooding … 使い続ける
- Maintenance-free … とにかく省力化する
- Write Code Every Day … 小さい変更を積み重ねる
P2P地震情報
P2P地震情報というサービスを開発・運用しています.
気象庁の地震情報やユーザ同士の「揺れた!」という地震感知情報を,P2Pネットワークによって共有するサービスです.
現在のサービス構成は下記の通りです.本体はP2Pですが,サーバの役割は多岐にわたります.なお,耐災害性の観点から,一部機能は有志の方々のご協力により全国数ヶ所に分散しています.
祝14周年🎉 (なお理想と現実は異なる)
2004年12月に公開し,今年で丸14年です.思った以上に長く続いています.
残念ながら,やりたいことは十分に実現出来ていません.GUIアプリケーションは未だにVB6製で,なかなかのレガシーです.*1
それでも,14年間続けることができました.なんだかんだで良かったと思っています.どうして続けられたのかというと,これから話す3つの技術が大事なのではと感じています.
サービスを続けていくための技術
Dogfooding: 使い続ける
サービスを続けるには,モチベーションを維持し続けることが重要です.
その最も簡単な方法が,自分自身がモチベーションの源泉となるように,サービスを自ら使い続けることだと感じています.
昔,LaTeXのオンラインコンパイラやPDFファイルの画像化といったサービスを公開していましたが,自分が使わなくなった途端にメンテナンスしなくなり,外部からアクセスは続いていたものの公開を終了したことがあります.
Maintenance-free: とにかく省力化する
モチベーション(と時間)を無駄にしないために,維持するためにかかるコストを最小限に抑えます.
色々ありますが,一番やってよかったのはひとりSlackでした.
サービスに関わるあらゆるメール,通知,実行ログをSlackに集約し,「とりあえずSlackの新着を見ればよい」という状態に辿り着きました.メールの未読消化と比べて圧倒的な省力化です.
Write Code Every Day: 小さい変更を積み重ねる
学生から社会人になり,「まとまった時間が取れない」「しかし週末に一気にやる気持ちにもなれない」という矛盾する悩みを抱えていました.数年間悩み続けた末,John Resigの記事に出会いました.
John Resig - Write Code Every Day (日本語訳参考: 毎日コードを書くこと - snowlongの日記)
学生時代の開発スタイルではいけないと気付かされました.
それから,100日以上毎日コードを書き続けました.1~2行しか書かない日もありましたが,それでも効果は絶大でした.
C#版のGUIアプリケーションも徐々に実装が進んでいて,一部機能は既に動作します.レガシーをそろそろ捨てられる日が来そうです.
楽しい個人開発ライフを!
世の中の便利な個人サービスが,少しでも長く続きますように…!
明日は @ktrkmk さんです.お楽しみに!
*1:余談ですが,Windows 10でもVisual Basic 6.0は動作しますし,IDEでコンパイルもできます
Raspberry Piでダッシュボードを作る(7) -時刻表-
Raspberry Piとミニディスプレイ,各種センサを使ってダッシュボードを作ります.
- Raspberry Piでダッシュボードを作る(1) -準備-
- Raspberry Piでダッシュボードを作る(2) -真っ白ダッシュボード-
- Raspberry Piでダッシュボードを作る(3) -デザイン-
- Raspberry Piでダッシュボードを作る(4) -天気予報-
- Raspberry Piでダッシュボードを作る(5) -温度・湿度(センサ)-
- Raspberry Piでダッシュボードを作る(6) -鉄道遅延情報-
- Raspberry Piでダッシュボードを作る(7) -時刻表- ←イマココ
今回のゴール
- 通勤に使っている最寄り駅の時刻表を表示させる.
いちいち最寄り駅の時刻表を調べるのは面倒です.ダッシュボードに常に表示させておきましょう.
時刻表の抽出
今回は Yahoo!路線情報 を利用します(※個人利用に留めましょう).例として 日比谷駅(東京メトロ日比谷線)の時刻表 を使ってみます.
情報の抽出には, Nokogiri
ライブラリを用います(スクレイピングとも言います).使い方は他所に譲るとして,下記スクリプトを parse.rb
といった名前で用意します.
#!/usr/bin/env ruby require 'net/http' require 'nokogiri' data = Nokogiri::HTML.parse(Net::HTTP.get(URI.parse(ARGV[0]))) data.css('table.tblDiaDetail > tr').each do |node| hour = node.css('td:first-child').text.rjust(2,"0") node.css('td:last-child > ul > li').each do |train| next if train.text !~ /(\d+)/ minute = $1.rjust(2,"0") puts "#{hour}:#{minute}" end end
実行してみましょう.
$ gem install nokogiri # Nokogiriライブラリをインストール $ chmod +x ./parse.rb # 実行権限を付与 $ ./parse.rb "https://transit.yahoo.co.jp/station/time/22951/?gid=3301&kind=1&done=time" 05:07 05:26 05:38 05:46 05:54 06:02 (省略)
無事に取得できました.ダッシュボードで表示するために,ファイルに出力しておきます.
$ ./parse.rb "https://transit.yahoo.co.jp/station/time/22951/?gid=3301&kind=1&done=time" > hibiya.txt
ダッシュボードへの表示
今回はかなりゴリ押しです.「むこう60分間の時刻表をいい感じに表示する」ようにします.
<div class="card"> <h4 class="card-header">時刻表</h4> <div class="card-body"> <% data = File.read("hibiya.txt") train_data = data.split(/\n/).map{|e| e.chomp}.select{|e| e>Time.now.strftime("%H:%M") && e<(Time.now+60*60).strftime("%H:%M")} .group_by{|e| e[0,2]}.map{|k,v| "<strong>#{k}</strong>#{" "*3}#{v.map{|e| e[3,2]}.join(" ")}"}.join("<br>") %> <p><%= train_data %></p> </div> </div>
これで,「次の電車何分だっけ?」というときにスマートフォンを操作せずに確認出来るようになりました.
補足
今回,土休日や種別(急行など)は考慮していません.
今日はここまで.
Xamarin.iOS: Storyboard上のアイテムとC#のクラスとをリンクさせる
Xamarin.iOSの日本語文献が少ないので,記録も兼ねてアウトプットしていこうと思います.
Storyboard上のアイテムをC#のクラスとリンクさせる
結論: Storyboardのプロパティ Class
にクラス名を書けば,対応する.cs
ファイルが自動的に作成される.
Storyboard上に配置したViewControllerや他アイテムに対してコードを書きたいとき,C#のクラスとリンクさせる必要があります.
このとき,Storyboard上でクラス名を指定すると,対応する .cs
ファイルが自動的に作成されます.
例
例えば,右側の Page View Controller をリンクさせたいときは,Page View Controller を選択した状態でクラス名を入力します.
クラス名に対応する.cs
ファイルが自動的に作成されます.
環境
- Visual Studio for Mac (Community) 7.6.3
- Xcode 9.4.1