20210502のC#に関する記事は4件です。

Selenium inputかbuttonか分からないボタンをクリック

画面のマニュアルしか持っていなくてHTMLソースをまだ見れないうちに、ボタンクリック動作を書く方法です。ボタンがinputかbuttonか分からないので「|」で結合しています。idやnameがあるかどうかも分からないのでボタン名での記述です。 「|」の前後の半角スペースはあってもなくても動きます。 HTML.html <input type="submit" value="登録" /> または <button type="submit">登録</button> C#.cs driver.FindElement(By.XPath("//input[@value='登録'] | //button[text()='登録']")).Click(); Java.java driver.findElement(By.xpath("//input[@value='登録'] | //button[text()='登録']")).click(); Python.py driver.find_element_by_xpath("//input[@value='登録'] | //button[text()='登録']").click()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ホストネームからIPアドレス(ipv4)を取得する方法

はじめに アプリ開発をしている際に、サーバーとのネットワークを接続状態をチェックする必要がありましたが、サーバー名(ホストネーム)からIPアドレスを取得しなければいけませんでした。 マイクロソフトのドキュメントにDns.GetHostEntry メソッドがありそれを参考に作成しました。 そこにこんなサンプルがありました。 // ホストネームからアドレスを取得する public static void DoGetHostEntry(string hostname) { IPHostEntry host = Dns.GetHostEntry(hostname); Console.WriteLine($"GetHostEntry({hostname}) returns:"); foreach (IPAddress address in host.AddressList) { Console.WriteLine($" {address}"); } } 問題点 このサンプルだと、ホストネームからアドレスを取得した際に、ipv6とipv4の両方を取得してしまいました。 自分が作成していたアプリでは、取得したアドレスをもとにpingでネットワークを確認するようにしていましたが、ipv6は時間がかかり、パフォーマンスに問題が起きてしまいました。 /// <summary> /// 指定されたIPアドレスのPingを確認する /// </summary> /// <param name="arg"></param> /// <returns></returns> private bool CheckPing(object arg) { Ping pingSender = new Ping(); PingOptions options = new PingOptions(); // Use the default Ttl value which is 128, // but change the fragmentation behavior. options.DontFragment = true; // Create a buffer of 32 bytes of data to be transmitted. string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; byte[] buffer = Encoding.ASCII.GetBytes(data); int timeout = 25; PingReply reply = pingSender.Send(arg.ToString(), timeout, buffer, options); if (reply.Status == IPStatus.Success) { return true; } return false; } 解決方法 サンプルで取得できたhost.AddressListのうち、どれがipv4か確認する方法がわかればいいです。 調べるとIPAddress.AddressFamily プロパティというものがありました。それを見ると、「IPv4 の場合は InterNetwork、IPv6 の場合は InterNetworkV6 を返します」とありました。 これを参考にして以下のようにサンプルを書き換えました。 private IPAddress DoGetHostEntry(string hostname) { IPHostEntry host = Dns.GetHostEntry(hostname); foreach (IPAddress address in host.AddressList) { if (address.AddressFamily == AddressFamily.InterNetwork) return address; } return null; } これで問題なくipv4のアドレスを取得して、ping確認することができるようになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ファイルの時間属性を変更したい![C#]

概要 今回はRaspberry PIではなくWindowsでファイルの属性である「作成日時、更新日時、アクセス日時」を指定した日時に変更するコマンドを作成したいと思います。 事前作業 ①デスクトップにVisual Studio communityバージョンをインストールします。 ダウンロードサイト(★コミュニティ★) https://visualstudio.microsoft.com/ja/downloads/ ②インストールオプションはすべてインストールも良いですが、アプリ開発をしないなら関連パッケージはインストールしない方がよいです。 プログラム内容 コマンドは基本的にパラメータを何も指定しない場合はコマンドの使い方のHELP内容が表示されるようにします。 ①ファイル情報を確認したい場合、下記のコマンドで確認します。 例)finfo filename ②ファイル情報の「作成日時、更新日時、アクセス日時」を変更したい場合、下記のコマンドで確認します。 例)finfo filename "作成日時" ※作成日時が「更新日時、アクセス日時」に設定されます。 例)finfo filename "作成日時" "更新日時" ※作成日時、更新日時が該当属性に更新されます。アクセス日時には「更新日時」が設定されます。 プログラムソース 下記のソースを「Windowsのコンソールアプリケーション(.Net Framework)」プロジェクトから作成します。 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace finfo { class Program { static void Main(string[] args) { //ファイル属性変更は最大3個の引数 if (args.Length >= 2) { fileEdit(args); } else if (args.Length == 1&& args[0] != "-h") { fileInfo(args[0]); } else { help(); } } public static void help() { Console.WriteLine("======================================="); Console.WriteLine("使い方"); Console.WriteLine("======================================="); Console.WriteLine("①ファイル情報出力の指定方"); Console.WriteLine("例)finfo filename"); Console.WriteLine("②ファイル情報出力の指定方"); Console.WriteLine("例)finfo filename \"YYYY-MM-DD HH:MM:SS\""); Console.WriteLine("例)finfo filename \"YYYY-MM-DD HH:MM:SS\" \"YYYY-MM-DD HH:MM:SS\""); Console.WriteLine("======================================="); } public static void fileInfo(string file) { if (!File.Exists(file)) { Console.WriteLine("指定したファイルは現在のフォルダに存在しません。"); return; } var info = new FileInfo(file); Console.WriteLine("======================================="); Console.WriteLine("ファイル情報"); Console.WriteLine("======================================="); Console.WriteLine("ファイル名 : " + info.Name); Console.WriteLine("ファイルパス : " + info.FullName); Console.WriteLine("作成日時 : " + info.CreationTime); Console.WriteLine("更新日時 : " + info.LastWriteTime); Console.WriteLine("サイズ : " + info.Length + " Bytes"); Console.WriteLine("アクセス日時 : " + info.LastAccessTime); Console.WriteLine("======================================="); } public static void fileEdit(string[] args) { DateTime createTime; DateTime writeTime; string file = "./" + args[0]; if (!File.Exists(file)) { Console.WriteLine("指定したファイルは現在のフォルダに存在しません。"); return; } //時間を設定 if (args.Length == 3) { createTime = Convert.ToDateTime(args[1]); writeTime = Convert.ToDateTime(args[2]); } else { createTime = Convert.ToDateTime(args[1]); writeTime = Convert.ToDateTime(args[1]); } var info = new FileInfo(file); //作成日時と更新日時を別々指定したい場合 if (createTime != null && writeTime != null) { File.SetCreationTime(file, createTime); File.SetLastWriteTime(file, writeTime); File.SetLastAccessTime(file, writeTime); } //作成日時と更新日時を同じ日時に指定したい場合 else { File.SetCreationTime(file, createTime); File.SetLastWriteTime(file, createTime); File.SetLastAccessTime(file, createTime); } Console.WriteLine("======================================="); Console.WriteLine("ファイル更新情報"); Console.WriteLine("======================================="); Console.WriteLine("ファイル名 : " + info.Name); Console.WriteLine("ファイルパス : " + info.FullName); Console.WriteLine("作成日時 : " + info.CreationTime); Console.WriteLine("更新日時 : " + info.LastWriteTime); Console.WriteLine("サイズ : " + info.Length + " Bytes"); Console.WriteLine("アクセス日時 : " + info.LastAccessTime); Console.WriteLine("======================================="); } } } 実行結果 下記の内容通りに動作することを確認しました。 C:\work\finfo\finfo\bin\Release>finfo.exe Program.cs ======================================= ファイル情報 ======================================= ファイル名 : Program.cs ファイルパス : C:\work\finfo\finfo\bin\Release\Program.cs 作成日時 : 2021/05/02 3:24:02 更新日時 : 2021/05/02 3:22:55 サイズ : 4681 Bytes アクセス日時 : 2021/05/02 3:24:03 ======================================= C:\work\finfo\finfo\bin\Release>finfo.exe Program.cs "2021/05/02 23:58:02" ======================================= ファイル更新情報 ======================================= ファイル名 : Program.cs ファイルパス : C:\work\finfo\finfo\bin\Release\Program.cs 作成日時 : 2021/05/02 23:58:02 更新日時 : 2021/05/02 23:58:02 サイズ : 4681 Bytes アクセス日時 : 2021/05/02 23:58:02 ======================================= C:\work\finfo\finfo\bin\Release>finfo.exe Program.cs "2021/05/02 23:58:02" "2021/05/02 23:59:32" ======================================= ファイル更新情報 ======================================= ファイル名 : Program.cs ファイルパス : C:\work\finfo\finfo\bin\Release\Program.cs 作成日時 : 2021/05/02 23:58:02 更新日時 : 2021/05/02 23:59:32 サイズ : 4681 Bytes アクセス日時 : 2021/05/02 23:59:32 ======================================= 終わりに これでファイルの属性を変更することができます。 どんなファイルも属性変更が可能です。 エクセルファイルまたは実行ファイルなども可能です。 (^^)//
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BASE64URL Encodingのすすめ

あのURLで見たBASE64の名前を僕達はまだ知らない。 あれは BASE64URL Encoding と言うらしい。 (どれだよ) Base64#変形版 - Wikipedia URLアプリケーションのための変形 Base64 ('base64url' encoding) 概要 BASE64 でエンコードした文字列には、 URL 上で意味を持つ + 、 / 、 = が含まれている。そのため URL で利用するためには工夫が必要である。BASE64 には BASE64URL Encoding と言う変形版が公式仕様にあり、URL Encoding や Percent Encoding よりも、URL上で利用するには都合が良い様に感じた。 今回WEBアプリで BASE64URL Encoding を扱うにあたり、ついでに BASE64 の自体も実装をしてみようと思い、やってみた、そんな話。 BASE64の解説 ① BASE64 (original) 読んでみると超シンプルな代物。 Base64 - Wikipedia バイナリ全体をbitの配列 (bit stream) として捉え、6bit ずつ区切り、対応する文字列に置き換える。 6bit = 2^6 = 64 (⇒ 64文字種) 文字テーブル: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ (64文字種) 3btyte (24bit) ずつ処理し、4byteに伸長する。※ 24は8と6の最小公倍数。 不足するbitは0で埋める。 不足するbyteは = 文字で埋める。 必ず文字列長は4の倍数になる。 コーディングの前に視覚化して整理するのも良いと思う。 ② BASE64 URL Encoding (URL安全なBASE64) BASE64 の仕様 RFC 4648 内で Section 5 「Base 64 Encoding with URL and Filename Safe Alphabet」 にて定義されている。 ・RFC 4648 - The Base16, Base32, and Base64 Data Encodings #Section-5 Base 64 Encoding with URL and Filename Safe Alphabet + 、 / 、 = は URL 上意味を持つ記号のため、それらの文字を回避したもの。 (URL上の役割は割愛。) 62番 + ⇒ - で表現する。 63番 / ⇒ _ で表現する。 = によるパディングは行わない。 結構シンプルで、ほぼ一緒。 # bit hex BASE64 BASE64URL # bit hex BASE64 BASE64URL 0 00000000 0x00 A A 32 00100000 0x20 g g 1 00000001 0x01 B B 33 00100001 0x21 h h 2 00000010 0x02 C C 34 00100010 0x22 i i 3 00000011 0x03 D D 35 00100011 0x23 j j 4 00000100 0x04 E E 36 00100100 0x24 k k 5 00000101 0x05 F F 37 00100101 0x25 l l 6 00000110 0x06 G G 38 00100110 0x26 m m 7 00000111 0x07 H H 39 00100111 0x27 n n 8 00001000 0x08 I I 40 00101000 0x28 o o 9 00001001 0x09 J J 41 00101001 0x29 p p 10 00001010 0x0A K K 42 00101010 0x2A q q 11 00001011 0x0B L L 43 00101011 0x2B r r 12 00001100 0x0C M M 44 00101100 0x2C s s 13 00001101 0x0D N N 45 00101101 0x2D t t 14 00001110 0x0E O O 46 00101110 0x2E u u 15 00001111 0x0F P P 47 00101111 0x2F v v 16 00010000 0x10 Q Q 48 00110000 0x30 w w 17 00010001 0x11 R R 49 00110001 0x31 x x 18 00010010 0x12 S S 50 00110010 0x32 y y 19 00010011 0x13 T T 51 00110011 0x33 z z 20 00010100 0x14 U U 52 00110100 0x34 0 0 21 00010101 0x15 V V 53 00110101 0x35 1 1 22 00010110 0x16 W W 54 00110110 0x36 2 2 23 00010111 0x17 X X 55 00110111 0x37 3 3 24 00011000 0x18 Y Y 56 00111000 0x38 4 4 25 00011001 0x19 Z Z 57 00111001 0x39 5 5 26 00011010 0x1A a a 58 00111010 0x3A 6 6 27 00011011 0x1B b b 59 00111011 0x3B 7 7 28 00011100 0x1C c c 60 00111100 0x3C 8 8 29 00011101 0x1D d d 61 00111101 0x3D 9 9 30 00011110 0x1E e e 62 00111110 0x3E + - 31 00011111 0x1F f f 63 00111111 0x3F / _ pad n/a n/a = n/a 実装 ① BASE64 ⇔ BASE64URLの相互変換 BASE64 Safe URL Convert.(Java) | Online editor and compiler 変換.java /** * URL安全なBASE64に変換. * * @param base64 * @return base64url */ private static String convertBase64SafeUrl(final String base64) { if(base64 == null) { return null; } else { String ret = base64; ret = ret.replaceAll("\\+", "-"); ret = ret.replaceAll("\\/", "_"); ret = ret.replaceAll("=+$", ""); return ret; } } /** * 通常 (URL非安全) のBASE64に変換. * * @param base64url * @return base64 */ private static String convertBase64UnSafeUrl(final String base64url) { if(base64url == null) { return null; } else { String ret = base64url; ret = ret.replaceAll("-", "+"); ret = ret.replaceAll("_", "/"); ret += "==="; ret = ret.substring(0, ((ret.length() / 4) * 4)); return ret; } } 変換結果 url safe :8KCut/CfpJTwn42G8J+RjQ== (24) >>>> 8KCut_CfpJTwn42G8J-RjQ (22) url unsafe :8KCut_CfpJTwn42G8J-RjQ (22) >>>> 8KCut/CfpJTwn42G8J+RjQ== (24) url safe :01234=== (8) >>>> 01234 (5) url safe :012345== (8) >>>> 012345 (6) url safe :0123456= (8) >>>> 0123456 (7) url safe :01234567 (8) >>>> 01234567 (8) url safe :a+/b==a+/b++a+/b//a+/b==a+/b==a+/b== (36) >>>> a-_b==a-_b--a-_b__a-_b==a-_b==a-_b (34) url safe :a+/b==a+/b++a+/b//a+/b==a+/b==a+/b (34) >>>> a-_b==a-_b--a-_b__a-_b==a-_b==a-_b (34) url unsafe :01234 (5) >>>> 01234=== (8) url unsafe :012345 (6) >>>> 012345== (8) url unsafe :0123456 (7) >>>> 0123456= (8) url unsafe :01234567 (8) >>>> 01234567 (8) url unsafe :012345678 (9) >>>> 012345678=== (12) url unsafe :a-_b==a-_b--a-_b__a-_b==a-_b==a-_b (34) >>>> a+/b==a+/b++a+/b//a+/b==a+/b==a+/b== (36) url unsafe :a-_b==a-_b--a-_b__a-_b==a-_b==a-_b (34) >>>> a+/b==a+/b++a+/b//a+/b==a+/b==a+/b== (36) url unsafe :?*+/-_=== (9) >>>> ?*+/+/====== (12) 文字列途中にある = については、そもそものルール違反のため、関知しない。 BASE64の文字についても、そもそものルール違反のため、関知しない。 元々末尾に = がついていた場合も、関知せずパディングを行う。 ② BASE64のEncode/Decode STEP① とりあえずBYTE配列をBASE64にするコードを作ってみる。 とりあえず、書いてみた。 6bitごとにbyte配列を整形する 処理と 文字列化するコード を分けて記述。 進数変換と同様の考え方。今回のbit を bit mask で抽出し、前回余ったbit とを論理和で合成する。 今回余ったbit は 左シフトし、不用bitを落としておき、次回に持ち越す。 今回取り出すbitの右シフト量 ( v ) は 2, 4, 6 の3パターン。 (⇒ (((i % 3) + 1) * 2) ) 次回に持ち越すbitの左シフト量は右シフト量 ( v ) に応じて求められ、 4, 2, 0 の3パターン。 (⇒ (6 - v) ) 上位2bitは落としておく。 左シフトのため下位bitは無視して良い。 6bitごとにbyte配列を整形するメソッド public static byte[] convert6bitBinary(byte[] data) { if(data == null) { return data; } else { byte mod = 0x00; List<byte> list = new List<byte>(); byte[] lowMaskList = new byte[] { 0x3F, // [0] 00111111 0x0F, // [1] 00001111 0x03, // [2] 00000011 }; for(int i = 0; i < data.Length; i++) { int j = i % 3; int v = ((j + 1) * 2); byte lowMask = lowMaskList[j]; byte b0 = data[i]; byte b6 = (byte)(mod | (lowMask & (b0 >> v))); list.Add(b6); mod = (byte)(0x3F & (b0 << (6 - v))); if(j == 2) { list.Add(mod); mod = 0; } } if((data.Length % 3) != 0) { list.Add(mod); } return list.ToArray(); } } 文字列化するコード public static string ToBase64(byte[] data, bool isUrlSafe = false) { StringBuilder sb = new StringBuilder(); if(data != null) { string base64_map; if(isUrlSafe) { base64_map = BASE64_MAP_URLSAFE; } else { base64_map = BASE64_MAP_BASIC; } foreach(byte b in data) { sb.Append(base64_map[b]); } if(!isUrlSafe) { int max; sb.Append("==="); max = (sb.Length / 4) * 4; sb.Remove(max, sb.Length - max); } } return sb.ToString(); } STEP② 4文字ごと に着目した処理を考え実装する。 BASE64は比較的古くからある仕様であり、展開や実装の容易さと言うのも兼ね備えていたと考える。なので、 = を用いたパディング は何のために必要なのか疑問語を覚えるが。これは逆説的に、4文字ごと に作業を行うことで、最適化が行える を可能性を感じられる。特に 4文字 で表現される 24 bit は 8 と 6 の 最小公倍数 である。即ち、3byte ずつ 4文字 にする のである。この程度であれば、下手にベタ書きしたした方が可読性もあり、 if文 も減り、高速化が望めそうである。そんな訳で、処理を見直して、Encode処理とDecode処理を組み直してみる。 ※ 現在のPCのCPUは計算機の発展であり、計算が得意で条件分岐が苦手である。レガシーなPCでは限られたリソースで速度を得るため、条件分岐を省き効率よく処理を作る必要性が高かった。 Encode (BASE64化) 3byteずつ、 int配列[3] にコピーする。byteの長さを超えた場合は、9bit目だけが立っている 0x0100 にする。 int配列[3] から byte配列[4] 6bitずつ格納していく。 (1) int配列[n]の9bit目 を byte配列[n]の7bit目 に論理和。 ※ 6bitに伸長した場合は7-8bit目は使わないので、存在しないバイトはbitを立てて64以上の値にする。 (2) 上位ビットはカレントとして int配列[n]の該当bit を byte配列[n - 1]の該当bit へ 左シフト + マスク (抽出) して 論理和 (3) 下位ビットは不足分として int配列[n - 1]の該当bit を byte配列[n - 1]の該当bit へ 右シフト + マスク (抽出) して 論理和 存在するバイト値は 0 ~ 63 の値になる。 ※ 0x0100 の下位8bitが0なので、不足分で使われても問題ない。 存在しないバイト値は 64 の値になる。 ※ これも、0x0100 の効果。 byte配列 の値を文字化して格納。 通常の BASE64 では変換表の 64番目 に = を入れておき、条件分岐無くパディング行う。 URL安全の BASE64 URL 場合は、64番目 以上はスキップします。 encode処理抜粋 private static readonly string BASE64_MAP_BASIC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; private static readonly string BASE64_MAP_URLSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; public static string encode(byte[] data, bool isUrlSafe = false) { StringBuilder sb = new StringBuilder(); for(int i = 0; i < data.Length; i += 3) { int[] src = new int[3]; byte[] tgt = new byte[4]; //copy or default. for(int j = 0; j < 3; j++) { int k = i + j; if(k < data.Length) { src[j] = data[k]; } else { //(bit: 0001 0000) src[j] = 0x0100; } } // reformat. tgt[0] = (byte)((0x00) | (0x00) | (0x3F & (src[0] >> 2))); tgt[1] = (byte)((0x00) | (0x3F & (src[0] << 4)) | (0x0F & (src[1] >> 4))); tgt[2] = (byte)((0x40 & (src[1] >> 2)) | (0x3F & (src[1] << 2)) | (0x03 & (src[2] >> 6))); tgt[3] = (byte)((0x40 & (src[2] >> 2)) | (0x3F & (src[2] << 0)) | (0x00)); // to character. if(isUrlSafe) { foreach(byte b in tgt) { if(b < 0x40) { sb.Append(BASE64_MAP_URLSAFE[b]); } } } else { sb.Append(BASE64_MAP_BASIC[tgt[0]]); sb.Append(BASE64_MAP_BASIC[tgt[1]]); sb.Append(BASE64_MAP_BASIC[tgt[2]]); sb.Append(BASE64_MAP_BASIC[tgt[3]]); } } return sb.ToString(); } 結構条件分岐が減って、良い感じ。また、符号化には最低でも2文字必要になるため、1-2文字目用のパディング判定処理は省いている。 Decode (BASE64からの複合) 通常の BASE64 と URL安全の BASE64 URL どちらでも複合ができる様にしている。 文字列 から byte配列[4] に値を格納する。存在しないbyte値は、64 にする。 通常の BASE64 では変換表の 64番目 に = を入れておき、こちらも 64 になる。 値が見つからなかった時は、URL安全の BASE64 URL の変換表も探索し、- _ を補完する。 byte配列[4] から int配列[3] へ 8bitずつ格納していく。 (1) byte配列[n + 1]の7bit目 を int配列[n]の9bit目 に論理和。 (2) 上位ビットはカレントとして byte配列[n]の該当bit を int配列[n]の該当bit へ 左シフト + マスク (抽出) して 論理和 (3) 下位ビットは不足分として byte配列[n + 1]の該当bit を int配列[n]の該当bit へ 右シフト + マスク (抽出) して 論理和 存在するバイト値の場合 byteの範囲を内の 0 ~ 255 の値になり、バイナリに追加する。 存在しないバイト値の場合、byteの範囲を超えた 256 (0x0100) の値になり、バイナリに追加しない。 decode処理抜粋 private static readonly string BASE64_MAP_BASIC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; private static readonly string BASE64_MAP_URLSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; public static byte[] decode(string base64) { List<byte> list = new List<byte>(); if(!string.IsNullOrEmpty(base64)) { for(int i = 0; i < base64.Length; i +=4) { byte[] src = new byte[4]; int[] tgt = new int[3]; for(int j = 0; j < 4; j++) { int k = i + j; byte code; if(k < base64.Length) { char c = base64[k]; int index = BASE64_MAP_BASIC.IndexOf(c); if(index < 0) { index = BASE64_MAP_URLSAFE.IndexOf(c); } code = (byte)index; } else { // 01000000 (64) code = 0x40; } src[j] = code; } tgt[0] = (0x00) | (0xFC & (src[0] << 2)) | (0x03 & (src[1] >> 4)); tgt[1] = (0xFF00 & (src[2] << 2)) | (0xF0 & (src[1] << 4)) | (0x0F & (src[2] >> 2)); tgt[2] = (0xFF00 & (src[3] << 2)) | (0xC0 & (src[2] << 6)) | (0x3F & (src[3] >> 0)); foreach(int val in tgt) { if(val < 0xFF) { list.Add((byte)val); } else { break; } } } } return list.ToArray(); } こちらも変換表の64番目に = を追加して64以上を使うことで、処理を少し簡略化出来ている感がある。 また、複合には最低でも2文字必要になるため、1byte目の複合には存在チェック用のbit処理を省いている。 本来 = 前の文字の場合は更にその前の余りを含んでいるので、 1文字の場合 ⇒ BASE64上ありえない (= で3文字パディング。) 2文字の場合 ⇒ = の 前の文字の下位4bitが0 (= で2文字パディング。) 3文字の場合 ⇒ = の 前の文字の下位2bitが0 (= で1文字パディング。) 4文字の場合 ⇒ 余りなし (= でパディングする必要もなし。) な感じになる。 実行例 BASE64 scrap (C#) | Online editor and compiler 実行例抜粋 // check strs[0]------------------------------------------------------- #source str : Hellow C# bin (hex) : 48 65 6C 6C 6F 77 20 43 23 bin (bit) : 01001000 01100101 01101100 01101100 01101111 01110111 00100000 01000011 00100011 #encode base64 : SGVsbG93IEMj base64url : SGVsbG93IEMj #decode (basic) SGVsbG93IEMj bin (hex) : 48 65 6C 6C 6F 77 20 43 23 bin (bit) : 01001000 01100101 01101100 01101100 01101111 01110111 00100000 01000011 00100011 str : Hellow C# #decode (urlsafe) SGVsbG93IEMj bin (hex) : 48 65 6C 6C 6F 77 20 43 23 bin (bit) : 01001000 01100101 01101100 01101100 01101111 01110111 00100000 01000011 00100011 str : Hellow C# // check strs[1]------------------------------------------------------- #source str : BASE64- bin (hex) : 42 41 53 45 36 34 2D bin (bit) : 01000010 01000001 01010011 01000101 00110110 00110100 00101101 #encode base64 : QkFTRTY0LQ== base64url : QkFTRTY0LQ #decode (basic) QkFTRTY0LQ== bin (hex) : 42 41 53 45 36 34 2D bin (bit) : 01000010 01000001 01010011 01000101 00110110 00110100 00101101 str : BASE64- #decode (urlsafe) QkFTRTY0LQ bin (hex) : 42 41 53 45 36 34 2D bin (bit) : 01000010 01000001 01010011 01000101 00110110 00110100 00101101 str : BASE64- // check strs[2]------------------------------------------------------- #source str : ???? bin (hex) : F0 A0 AE B7 F0 9F A4 94 F0 9F 8D 86 F0 9F 91 8D bin (bit) : 11110000 10100000 10101110 10110111 11110000 10011111 10100100 10010100 11110000 10011111 10001101 10000110 11110000 10011111 10010001 10001101 #encode base64 : 8KCut/CfpJTwn42G8J+RjQ== base64url : 8KCut_CfpJTwn42G8J-RjQ #decode (basic) 8KCut/CfpJTwn42G8J+RjQ== bin (hex) : F0 A0 AE B7 F0 9F A4 94 F0 9F 8D 86 F0 9F 91 8D bin (bit) : 11110000 10100000 10101110 10110111 11110000 10011111 10100100 10010100 11110000 10011111 10001101 10000110 11110000 10011111 10010001 10001101 str : ???? #decode (urlsafe) 8KCut_CfpJTwn42G8J-RjQ bin (hex) : F0 A0 AE B7 F0 9F A4 94 F0 9F 8D 86 F0 9F 91 8D bin (bit) : 11110000 10100000 10101110 10110111 11110000 10011111 10100100 10010100 11110000 10011111 10001101 10000110 11110000 10011111 10010001 10001101 str : ???? // check 1st impl. encode : 6A 05 39 => agU5 encode : 6A 05 => agU= encode : 6A => ag== encode : 6A 05 39 => agU5 encode : 6A 05 => agU encode : 6A => ag decode : agU5 => 6A 05 39 decode : agU= => 6A 05 decode : ag== => 6A decode : a=== => decode : agU5 => 6A 05 39 decode : agU => 6A 05 decode : ag => 6A decode : a => // check 2nd impl. encode : 6A 05 39 => agU5 encode : 6A 05 => agU= encode : 6A => ag== encode : 6A 05 39 => agU5 encode : 6A 05 => agU encode : 6A => ag decode : agU5 => 6A 05 39 decode : agU= => 6A 05 decode : ag== => 6A decode : a=== => decode : agU5 => 6A 05 39 decode : agU => 6A 05 decode : ag => 6A decode : a => BASE64URL の活用法 私が今回使おうと思った背景では、Servlet のWEBアプリケーションで ChromeのAcrobat拡張 を利用するために、リクエストをURLに再現性のない POST から再現性のある GET に変更しようと思ったのがある。パラメータは複数あり、あまり生で見せたくないというのもあり、全パラメータをまとめて BASE64 での シリアライズ を行うことにした。 ChromeのAcrobat拡張 ではURLを再度開いているだけのため、再現性のあるURLである必要がある。これはfirefoxで内蔵ビューアで開いたPDFをオンザフライでAdobeReaderなどに開かせる際も同様かと思う。 GETによるアプリケーション連携が増えている昨今。一時的なリクエストにせよ、GET化することのメリットがそれなりに出てきている。 と言うか、私は パーマネント リンク大好きマン なので参照メインのサイトで パーマネント リンク 取れない時に結構キレてます。えぇ、最近のアニメ公式サイトとか多いですね。。。すっごいゴテゴテで、友人にshareしようとしたら、 パーマネント リンク が取れなくてTOPページしか送れないこととか。。。 ダイミダラーの公式サイト を見習って欲しい (そういう意味ではFLASHが死に絶えたのはほんと、良かったのかも知れない。FLASHだけで作ってるサイトも度々あったので) パーセント エンコーディング や URI エンコーディング じゃダメだったのか? パーセント エンコーディング 及び URI エンコーディングはどんな文字列にもエンコード/デコードを処理をかけることができ、符号化/復号化がブラウザやWEBサーバーのリクエスト処理に含まれてしまっている。そのため、間に認証ページなどでジャンプが挟まる様な場合、ジャンプ数を加味して多重でパーセント エンコーディングをかけたりする必要がある場合がある。特に = が含まれている場合には、多段でジャンプ中にQueryStringとして前のページでのパラメータとして喰われてしまい、自分のページに届かない、なんてこともある。BASE64 の場合は + / により、多段ジャンプ自体が阻害される場合もある。 BASE64URL では自分で展開処理を行わなければいけないデメリット自体がそのままメリットとなり、他のページとの競合を避けることができる。誰も勝手に複合したりしないので、最後までそのまま自分のところに届く。 多段ジャンプ(笑) と思うこともあるかも知れないが。案外Twittterの認証など色々画面見てても、結構パラメーターを引きずっていることが多くあると思い、そんなにマイノリティでは無いかと思う。 HTTPのリクエストのセキュリティは、GET 以上のものが POST で得られるもんでも無いと思うので、もっと別の形で確保してください。 (だから認証でも GET を使っているのでしょう。) 総括 ベンチマークは取らないが、BASE64の実装をして理解を深めることが出来たと思います。とてもシンプルなロジックなので、一度自分で書いてみるのは、bit演算の訓練にもなって良いかと思った。あと、行数によってはベタに書くことも悪く無く、今回の BASE64 もその類なのかと思う。 なお、JavaとC#が混在しているのは単に気分です。( 当方、final教信者 の C#er です )。 参考 (謝辞) Base64 - Wikipedia https://ja.wikipedia.org/wiki/Base64 RFC 4648 - The Base16, Base32, and Base64 Data Encodings https://tools.ietf.org/html/rfc4648
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む