base大家族详解
base大家族详解
*/-->
pre.src {background-color: #292b2e; color: #b2b2b2;}
pre.src {background-color: #292b2e; color: #b2b2b2;}
base大家族详解
1 简介
对于base编码的使用,参考Base What? A Practical Introduction to Base Encoding。
- base2: 2进制
- base10: 10进制
- base16: 计算机字节表示
- base32: 字母加数字
- base64: 更大的表示范围
- base58: bitcoin为了比base64更易读采用的表示
这些base编码主要是在计算机的二进制表示,和人可读的字符表示之间转换。
具体实现可以分为2种,能利用bit位直接转换的base(例如base2, base16,base32, base64),和不能利用bit位直接转换的base(例如base10, base58)。
2 实现代码
全部采用clojure实现,主要是为了理解base编码,只求代码容易理解,性能不是很好:
- (ns base)
- (defn indices-of
- [f coll]
- (keep-indexed #(if (f %2) %1 nil) coll))
- (defn first-index-of
- [f coll]
- (first (indices-of f coll)))
- (defn find-thing
- "查找`coll`中第一个等于value的索引"
- [value coll]
- (first-index-of #(= % value) coll))
- (def divmod "返回[商 余数]" (juxt quot mod))
- ;; 1. 不能利用bit位直接转换的base
- ;; 基本编码方法就是把输入作为一个数字,不断的对baseN的N进行除法运算,
- ;; 每次的余数就是base-table的索引,商作为下一次的除数进行计算。直到除尽
- (defn base-encode-num
- "base编码一个数字`n`
- `leading-zeros`为padding个数
- `base-table`为编码转换表"
- [n leading-zeros base-table]
- (let [base-count (count base-table)]
- (loop [n n
- result ""]
- (if (>= n base-count)
- (let [[d m] (divmod n base-count)]
- (recur d
- ;; 编码是从后往前进行的,每次除以baseN的N,
- ;; 以余数作为索引
- (-> (nth base-table m)
- (str result))))
- (->> (if (pos? n)
- (-> (nth base-table n)
- (str result))
- result)
- ;; 注意,因为是从后往前进行生成的,padding在前面添加
- (concat (repeat leading-zeros (first base-table)))
- (apply str))))))
- (defn base-enc
- "编码base10,base36,base58等"
- [base-table s]
- (let [s-bytes (.getBytes s)
- leading-zeros (->> s-bytes
- (take-while zero?)
- count)]
- (-> (java.math.BigInteger. s-bytes)
- (base-encode-num leading-zeros base-table))))
- (defn invert-table
- "生成`table`序列的反向索引表 "
- [table]
- (into {}
- (map #(vector %1 %2)
- table
- (iterate inc ))))
- ;; baseN的解码方法就是对输入字符串的每个字符查找索引 * (N ** 输入串反向顺序的索引),
- ;; 结果累加就是目标数字,再把数字还原为字符串
- (defn base-dec
- "解码base10,base36,base58等"
- [base-table s]
- (let [padding (->> s
- (take-while #(= % (first base-table)))
- (map (constantly (byte ))))
- inverted-table (invert-table base-table)
- base-count (count base-table)]
- (->> (reverse s)
- (reduce (fn [[r m] c]
- [(+ r (*' m (inverted-table c)))
- (*' m base-count)]) [ ])
- first
- str
- java.math.BigInteger.
- .toByteArray
- (drop-while zero?)
- (concat padding)
- byte-array
- String.)))
- (def base58-table "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
- (def enc-base58 (partial base-enc base58-table))
- (def dec-base58 (partial base-dec base58-table))
- (def base36-table "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
- (def enc-base36 (partial base-enc base36-table))
- (def dec-base36 (partial base-dec base36-table))
- (def base10-table "0123456789")
- (def enc-base10 (partial base-enc base10-table))
- (def dec-base10 (partial base-dec base10-table))
- (comment
- (def target "D9cS9N9iHjMLTdA8YSMRMp")
- (def r (dec-base58 target))
- (= target (enc-base58 r))
- (= "64021609848" (enc-base36 "testabc"))
- (enc-base10 "this is a test")
- ;; => "2361031878030638688519054699098996"
- (dec-base10 "2361031878030638688519054699098996")
- ;; => "this is a test"
- )
- ;;; ============= 以2为基的base算法,base2, base4, base16, base32, base64 ...
- ;;; 可以利用bit位直接变换的base
- ;;; 算法把输入的8bit串转换为base(2^n)的nbit串,不需要做除法,
- ;;; 编码过程 直接把输入字符串转换为bit序列,再根据baseN的位宽(如base64 6位,base32 5位)重新划分,
- ;;; 查表获取目标串,不需要除法,因此要比上面的base算法速度快
- ;;; 下面统一称为base2
- (defn bits
- "转换数字为bit序列,如果不指定`width`,默认为8位"
- ([n] (bits n ))
- ([n width]
- (reverse (map #(bit-and (bit-shift-right n %) ) (range width)))))
- (defn numb
- "bit序列转换为数字"
- [bits]
- (BigInteger. (apply str bits) ))
- (defn string-to-bits
- "字符串转换为bit序列"
- [msg]
- (->> (.getBytes msg)
- (map #(bits %))
- flatten))
- (defn padding-count
- "获取字节序列`v`的padding个数"
- [v]
- (->> (vec v)
- rseq
- (take-while zero?)
- count))
- (defn drop-padding
- "去掉字节序列`v`的padding"
- [v]
- (-> (padding-count v)
- (drop-last v)))
- (defn log2
- [n]
- (/ (Math/log n)
- (Math/log )))
- (defn get-bit-width
- "获取`base-table`的位宽,即用多少字节表示一个字符"
- [base-table]
- (let [width (-> (count base-table)
- log2)]
- (when (= width (Math/floor width))
- (int width))))
- (defn valid-base2-table?
- "检测是否为有效的base2表"
- [base-table]
- (-> (get-bit-width base-table)
- nil?
- not))
- (defn byte-align
- "根据位宽bit-width,获取需要对齐的base字节数
- 比如一个字节是8位,转换base64,每个base64字节为6位
- byte-align就是确定最低需要多少个base64的6位字节才能对齐到8位字节"
- [bit-width]
- (let [max-padding
- fn-len-range (fn [width]
- (map (partial * width) (range max-padding)))
- hex-range (fn-len-range )
- bit-range (fn-len-range bit-width)
- align-value (->> (clojure.set/intersection (set hex-range) (set bit-range))
- (apply min))]
- (/ align-value bit-width)))
- (defn hex-byte-align
- "获取最低需要多少个hex字节才能对齐"
- [bit-width]
- (-> (byte-align bit-width)
- (* bit-width)
- (/ )))
- (comment
- (byte-align )
- ;; => 4
- (hex-byte-align )
- ;; => 3
- ;; base64需要4个6位字节才能对齐到3个8位hex字节,即 4 * 6 = 3 * 8
- (byte-align )
- ;; => 8
- (hex-byte-align )
- ;; => 5
- ;; base32需要8个5位字节才能对齐到5个8位hex字节, 即 8 * 5 = 5 * 8
- (hex-byte-align )
- ;; => 1
- (hex-byte-align )
- ;; => 1
- (hex-byte-align )
- ;; => 1
- ;; 可以看到base2, base4,base16 可以对齐到1个字节,不需要padding
- )
- (defn gen-padding
- "生成`data`的padding串"
- [bit-width data]
- (let [len (count data)
- align (byte-align bit-width)
- aligned-byte (mod len align)]
- (if (zero? aligned-byte)
- ""
- (apply str
- (-> (- align aligned-byte)
- (repeat "="))))))
- (defn base2-enc
- "编码base2类型的字符串`s`
- `base-table`必须是以2为基的base表,base(2^n),例如base16,base32,base64"
- [base-table s]
- {:pre [(valid-base2-table? base-table)]}
- (let [bits (string-to-bits s)
- base2-char #(->> (numb %)
- (nth base-table))
- bit-width (get-bit-width base-table)
- data (partition bit-width bit-width (repeat ) bits)]
- (str (->> (map base2-char data)
- (apply str))
- (gen-padding bit-width data))))
- (defn base2-dec
- "base2解码,`s`为要解码的字符串
- `base-table` 解码表"
- [base-table s]
- {:pre [(valid-base2-table? base-table)]}
- (let [bit-width (get-bit-width base-table)
- inverted-table (merge (invert-table base-table)
- {\= } ;; 添加padding字符
- )]
- (->> (mapcat #(some-> (inverted-table %1)
- (bits bit-width))
- s)
- ;; mapcat自动过滤掉base2-bits的nil值,即忽略s串中不属于base-table的字符
- (partition (repeat ))
- (map numb)
- drop-padding
- (map char)
- (apply str))))
- ;; base64,base32 需要使用base2-enc dec
- (def base64-table "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
- (def enc-base64 (partial base2-enc base64-table))
- (def dec-base64 (partial base2-dec base64-table))
- (def base32-table "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
- (def enc-base32 (partial base2-enc base32-table))
- (def dec-base32 (partial base2-dec base32-table))
- ;; base16等同于hex转换
- (def base16-table "0123456789ABCDEF")
- (def enc-base16 (partial base2-enc base16-table))
- (def dec-base16 (partial base2-dec base16-table))
- ;; base2是2进制表示
- (def base2-table "01")
- (def enc-base2 (partial base2-enc base2-table))
- (def dec-base2 (partial base2-dec base2-table))
- ;; 自动定义base的宏
- (defmacro defbase
- "根据base-table生成base编码和解码函数的定义"
- [base-name base-table]
- (let [table-name (symbol (str base-name "-table"))
- enc-fname (symbol (str "enc-" base-name))
- dec-fname (symbol (str "dec-" base-name))
- def-table `(def ~table-name ~base-table)]
- (if-let [bit-width (get-bit-width base-table)]
- `(do ~def-table
- (def ~enc-fname (partial base2-enc ~base-table))
- (def ~dec-fname (partial base2-dec ~base-table)))
- `(do ~def-table
- (def ~enc-fname (partial base-enc ~base-table))
- (def ~dec-fname (partial base-dec ~base-table))))))
- ;; (defbase base64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
- ;; (defbase base32 "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
- ;; (defbase base16 "0123456789ABCDEF")
- ;; (defbase base2 "01")
- ;; (defbase base58 "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
- ;; (defbase base36 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
- ;; (defbase base10 "0123456789")
- (defbase base8 "01234567")
- (comment
- base8-table
- ;; => "01234567"
- (enc-base8 "test 1")
- ;; => "3506256335020061"
- (dec-base8 *1)
- ;; => "test 1"
- (= (padding-count [ ]))
- (= (padding-count [ ]))
- (= (padding-count []))
- (= [ ] (drop-padding [ ]))
- (= "I" (base2-dec base64-table "SQ=="))
- (= "AM" (base2-dec base64-table "QU0="))
- (base2-enc base64-table "I")
- ;; => "SQ=="
- (base2-enc base64-table "AM")
- ;; => "QU0="
- (String. (base64/encode "I"))
- ;; => "SQ=="
- (String. (base64/encode "AM"))
- ;; => "QU0="
- (= "this is a test" (base2-dec base32-table "ORUGS4ZANFZSAYJAORSXG5A="))
- (base32/encode "this is a test")
- ;; => "ORUGS4ZANFZSAYJAORSXG5A="
- (base2-enc base32-table "this is a test")
- ;; => "ORUGS4ZANFZSAYJAORSXG5A="
- ;; base-table不符,产生异常
- (= "base58_is_boring" (base2-dec base58-table "D9cS9N9iHjMLTdA8YSMRMp"))
- (= (get-bit-width base64-table))
- (= (get-bit-width base32-table))
- (= nil (get-bit-width base58-table))
- (enc-base32 "I") ;; base32的padding比较多
- ;; => "JE======"
- (dec-base32 *1)
- ;; => "I"
- ;; base16相当于内存hex表示
- (enc-base16 "this is a test")
- ;; => "7468697320697320612074657374"
- (dec-base16 "7468697320697320612074657374")
- ;; => "this is a test"
- (dec-base16 "7468IIOO697320697320612074657374") ;; 不合法字符直接忽略
- ;; => "this is a test"
- (codecs/bytes->hex (codecs/str->bytes "this is a test"))
- ;; => "7468697320697320612074657374"
- ;; base2相当于内存的二进制表示
- (enc-base2 "t")
- ;; => "01110100"
- ;; \t的二进制
- (Integer/toString (int \t) )
- ;; => "1110100"
- (dec-base2 "01110100")
- ;; => "t"
- ;; 注意解码时前面的0是必要的
- (def s1 "this is a test")
- (= (enc-base64 s1) (String. (base64/encode s1)))
- (= (enc-base32 s1) (base32/encode s1))
- )
- ;;; ============== 自动base解码
- (defn valid-base-str?
- "检查字符串s是否符合base-table"
- [base-table s]
- (clojure.set/subset? (set s)
- (-> (set base-table)
- (conj \=))))
- (defn guess-base
- [text]
- (cond
- ;; 这里必须按从小到大的顺序测试
- (valid-base-str? base16-table text) :base16
- (valid-base-str? base32-table text) :base32
- (valid-base-str? base64-table text) :base64
- :else :unknown))
- (defn decode
- "自动base解码"
- [text & {:keys [step debug]
- :or {step
- debug false}}]
- (loop [text text
- step step]
- (when debug
- (println "step" step "decode:" text))
- (case (guess-base text)
- :base16 (do (println "step" step "--> base16")
- (-> (dec-base16 text)
- (recur (inc step))))
- :base32 (do (println "step" step "--> base32")
- (-> (dec-base32 text)
- (recur (inc step))))
- :base64 (do (println "step" step "--> base64")
- (-> (dec-base64 text)
- (recur (inc step))))
- :unknown (do (println "result:" text)
- text))))
- (comment
- (def text "3441353234343435353735323442353634423539354134353336353333323536343735323444343535333537344234433441343634353535353535323533343734413335344134363439353534423530344135413437353634463444353334463441344534443435333235363533353534423531354134363431353334413335")
- ;; 经测试,直接转换bits对于长字符串速度比较慢
- (decode text)
- ;; step 1 --> base16
- ;; step 2 --> base16
- ;; step 3 --> base32
- ;; step 4 --> base32
- ;; step 5 --> base64
- ;; step 6 --> base64
- ;; result: key:hi!venus
- ;; => "key:hi!venus"
- )
3 结论
base编码基本是码表转换,产生人可读的字符表示,不适合加密。
作者: ntestoc
Created: 2019-04-11 四 10:28
base大家族详解的更多相关文章
- 基于CentOS6.5下snort+barnyard2+base的入侵检测系统的搭建(图文详解)(博主推荐)
为什么,要写这篇论文? 是因为,目前科研的我,正值研三,致力于网络安全.大数据.机器学习研究领域! 论文方向的需要,同时不局限于真实物理环境机器实验室的攻防环境.也不局限于真实物理机器环境实验室的大数 ...
- 基于Windows7下snort+apache+php 7 + acid(或者base) + adodb + jpgraph的入侵检测系统的搭建(图文详解)(博主推荐)
为什么,要写这篇论文? 是因为,目前科研的我,正值研三,致力于网络安全.大数据.机器学习.人工智能.区域链研究领域! 论文方向的需要,同时不局限于真实物理环境机器实验室的攻防环境.也不局限于真实物理机 ...
- Xamarin.Android通知详解
一.发送通知的机制 在日常的app应用中经常需要使用通知,因为服务.广播后台活动如果有事件需要通知用户,则需要通过通知栏显示,而在Xamarin.Android下的通知需要获取Notification ...
- Android 网络框架之Retrofit2使用详解及从源码中解析原理
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
- 基础拾遗------redis详解
基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...
- 好用的wget命令从下载添加环境变量到各参数详解
本文是因为(笔者使用的windows系统)使用过好几次wget后,始终存在各种细节问题,于是下定决定细致的研究一下,并记录下其中细节. 下载与安装 第一步:下载wget,网络地址:http://dow ...
- PHP 进程详解
.note-content { font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", STHeit ...
- linux查看端口及端口详解
今天现场查看了TCP端口的占用情况,如下图 红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说E ...
- 【转】Inode详解
Inode详解 转自: Inode详解 一.inode是什么 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存 ...
随机推荐
- 为什么java需要序列化对象
序列化是一种用来处理对象流的机制 所谓对象流:就是将对象的内容进行流化,可以对流化后的对象进行读写操作,也可将流化后的对象传输与网络之间 序列化是为了解决在对象流进行读写操作时所引发的问题 序列化的实 ...
- 使用crontab调度任务
复杂的.分布式的.工作流式的调度可以通过azkaban来进行调度,除了执行调度任务之外,它还能进行定时调度.而对于简单的服务器任务,如执行一个小脚本,发送邮件等,可以使用crontab命令直接进行,在 ...
- 面向连接的传输TCP(一)
这篇博客主要是对计算机网络自顶向上做的阅读笔记,深入地了解TCP 一.TCP连接 1.特点: a.TCP是面向连接的,因为一个进程在向另一个进程进行数据传输之前必须先要握手,即要互相发送报文,以确认信 ...
- JVM之---Java源码编译机制
Sun JDK中采用javac将Java源码编译为class文件,这个过程包含三个步骤: 1.分析和输入到符号表(Parse and Enter) Parse过程所做的工作有词法和语法分 ...
- 项目Debug版本与Release版本的区别
Debug版本:通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序. Release版本:称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很 ...
- python数据类型之字典
字典定义 字典是一种 key-value 的数据类型,这点很重要,是区别使用列表和字典的依据. 语法格式: info = { 'stu1101': "Aaron", 'stu110 ...
- FineReport中如何实现自动滚屏效果
对于一些特殊的模板,可能为了展示的更加丰富.全面会在一个页面放置很多图表.表格等内容.由于内容过多,超出了浏览器窗口的大小导致内容展示不全的情况.这样我们就需要用到JS滚屏效果来解决,这里主要介绍在F ...
- Surface电池阈值
Surface电池阈值 笔记本电脑一般都会提供一个电池保养的软件,其主要最用是让电池在插电情况下保持在50%-80%之间,以延长电池寿命,减少电池损耗.而微软自家的Surface却一直没有这个设置. ...
- Ubunt 安装mysql
apt-get install mysql-client-core-5.6apt-get install mysql-client-5.6apt-get install mysql-server-5. ...
- 0 Linux下Java使用ProcessBuilder执行命令与直接Bash执行命令之间的不同(环境变量方面)
0 问题发生 xiaojietest.java package tasks; import java.io.BufferedReader; import java.io.BufferedWriter; ...