1. 程式人生 > >Quoted-Printable編碼原理及程式碼實現

Quoted-Printable編碼原理及程式碼實現

這篇文章是我之前在RYTong內部分享的一篇文章,摘取了有用的部分。當時幫助某專案郵件系統解決問題,期間瞭解到Quoted-Printable編碼,在此與大家分享下該編碼的原理和個人版本的程式碼實現。

關於規範

關於Quoted-Printable的編碼規範,需要參考rfc2045

為了方便大家閱讀,在此給大家看一下融合我個人理解的翻譯:

  1. 除了換行(CRLF序列)中的CR或LF,所有(8bit)位元組都可以表示為符號”=”後跟兩個16進位制數字(16進位制數字表示該位元組的值)的格式,16進位制數字只能有數字和大寫ABCDEF表示,不能用小寫字母。例如,ASCII碼換頁符,數值為12,編碼後表示為”=0C”。

  2. 值為33到126的位元組(除61外),包含邊界,也可以不進行編碼。

  3. 製表符(值為9)和空格(值為32)可以不進行編碼,但未編碼的兩者不能位於編碼後的行尾。如果行尾由軟換行符(符號”=”,參照規則5)時,軟換行符前的製表符和空格可以不編碼。兩者若在行尾則必須編碼。

  4. 換行(CRLF序列)不必進行編碼。因為只有CRLF序列表示換行,因此包含CR或LF的其他序列須進行編碼。

  5. Quoted-Printable編碼要求編碼後的文字每行長度不得超過76字元。如果原文的一行被編碼後的文字長度超過76,那麼需要在編碼後的行尾新增軟換行符”=”,表示原文此處沒有換行。注意軟換行符”=”也會計入編碼後的位元組長度,也就是說如果編碼後行尾存在軟換行符,那麼該行其他字元數不得超過75。

  6. 建議對-!\”#[email protected][\]^`{|}~進行編碼,以避免對其他協議或少數閘道器(如EBCDIC)的處理造成影響。

  7. 建議對CRLF序列進行編碼,避免各平臺不同的換行序列對解碼造成影響。

關於程式碼實現

以下是本人基於以上規則實現的程式碼(Erlang實現)

-module(qp_encoder).

-export([qp_encode/1]).

-define(LINE_LENGTH, 76).
-define(is_suggested(C),
        C == 45 orelse C == 64 orelse C == 96 orelse C
== 46 orelse (C >= 33 andalso C =< 36)
orelse (C >= 91 andalso C =< 94) orelse (C >= 123 andalso C =< 126))
. -define(CR, 13). -define(LF, 10). -define(HT, 9). -define(SPACE, 32). %%%%%%%%%%%%%%%%%%%%%%%%%% API %%%%%%%%%%%%%%%%%%%%%%%%%% %% @spec (Input :: binary() | string()) -> Output :: binary() qp_encode(String) when is_list(String) -> qp_encode(list_to_binary(String)); qp_encode(Bin) when is_binary(Bin) -> qp_encode(0, Bin, <<>>, <<>>, <<>>). %% @spec (Input :: binary() | string()) -> Output :: binary() qp_decode(String) when is_list(String) -> qp_decode(list_to_binary(String)); qp_decode(Bin) when is_binary(Bin) -> qp_decode(Bin, <<>>, 1, 1). %%%%%%%%%%%%%%%%%%%%%%%%%% internal %%%%%%%%%%%%%%%%%%%%%%%%%% %% Num is the total number of characters of LineAcc + LastBuf %% Bin is the input binary to be encoded %% LastBuf is the encoded binary of last character %% LineAcc is the current line of encoded binary %% Acc is the output binary qp_encode(Num, Bin, LastBuf, LineAcc, Acc) when Num >= ?LINE_LENGTH -> {CurrentLine, LastBuf2, NextLineNum} = qp_new_line(LastBuf, LineAcc), qp_encode(NextLineNum, Bin, LastBuf2, <<>>, <<Acc/binary, CurrentLine/binary>>); qp_encode(_, <<>>, LastBuf, LineAcc, Acc) -> Encoded = case LastBuf of <<C>> when C == ?HT orelse C == ?SPACE -> do_qp_encode(C); _ -> LastBuf end, <<Acc/binary, LineAcc/binary, Encoded/binary>>; qp_encode(Num, <<C, Rest/binary>>, LastBuf, LineAcc, Acc) -> {EncNum, Enc} = inline_qp_encode(C), qp_encode(Num + EncNum, Rest, Enc, <<LineAcc/binary, LastBuf/binary>>, Acc). %% nethier HT nor SPACE could be presented at the end of an encoded line qp_new_line(<<C>>, CurrentLine) -> {<<CurrentLine/binary, $=, ?CR, ?LF>>, <<C>>, 1}; qp_new_line(LastBuf, CurrentLine) -> {<<CurrentLine/binary, $=, ?CR, ?LF>>, LastBuf, 3}. %% characters(-!\"#[email protected][\]^`{|}~.) are suggested to be encoded inline_qp_encode(C) when ?is_suggested(C) -> {3, do_qp_encode(C)}; %% character "=" must be encoded inline_qp_encode($=) -> {3, do_qp_encode($=)}; %% character whose ascii code is between 33 and 126 inclusively need not %% be encoded inline_qp_encode(C) when C >= 33 andalso C =< 126 -> {1, <<C>>}; %% HT and SPACE need not be encoded inline_qp_encode(C) when C == ?HT orelse C == ?SPACE -> {1, <<C>>}; %% others must be encoded inline_qp_encode(C) -> {3, do_qp_encode(C)}. do_qp_encode(Int) when is_integer(Int) -> [Hex1, Hex2] = case integer_to_list(Int, 16) of [H] -> [$0, H]; _Val -> _Val end, <<$=, Hex1, Hex2>>.

第一條規則由do_qp_encode/1函式實現,這部分值得注意的是integer_to_list/2方法的使用,可以直接將一個整數轉成對應的16進位制字串。
第二條規則由67到70行的程式碼實現,此處用了Erlang模式匹配的技巧,由於會優先匹配68行的函式,因此72行函式判斷條件並不需要將“=”號排除。
第三條規則由75和76行的程式碼實現,大家可能會覺得此處邏輯存在問題,因為編碼後行尾是不能存在未編碼的製表符(值為9)和空格(值為32)的。這裡是因為我按規則7將CRLF進行了編碼,因此編碼後的每行都是以“=”結束的軟換行,所以我並未對製表符和空格進行編碼。由於最後一行的編碼內容是不存在軟換行的,因此我加了46到51行的程式碼處理,將最後一行末尾出現的製表符和空格進行了編碼。
第四條規則和第七條有衝突,這裡我按第7條進行了實現。
第五條規則由40到43行程式碼實現。
第六條規則由65和66行程式碼實現。大家可以注意到,我在程式碼邏輯裡增加了對“.”號的編碼,這是由於我們發現在實際使用(郵件轉發)中,發現部分出現在行首的“.”號會被其他伺服器刪除掉(原因未知),因此特意對該符號進行了編碼,編碼之後此問題解決。

除了Quoted-Printable編碼規則外,大家還可以通過這個例子瞭解一下Erlang binary的相關語法,用起來還是很方便的。有興趣的同學可以先學習一下《Programming Erlang》這本書的5.2和5.3章節。

相關推薦

Quoted-Printable編碼原理程式碼實現

這篇文章是我之前在RYTong內部分享的一篇文章,摘取了有用的部分。當時幫助某專案郵件系統解決問題,期間瞭解到Quoted-Printable編碼,在此與大家分享下該編碼的原理和個人版本的程式碼實現。 關於規範 關於Quoted-Printable的

OpenCV(一)——高斯卷積核原理程式碼實現

貼出getGaussianKernel原始碼 在smooth.cpp中 提示:Gaussian核基於 正態分佈函式設計 μ是均值,σ^2是方差 正態函式(即一維Gaussian卷積核)如下 二維卷積核通過對一維積分得到,並且μ = 0 根據如下原始碼可知

微信公眾號掃碼登陸原理程式碼實現

1.使用者開啟公眾號點選掃碼功能(注意我們用 scancode_waitmsg這種型別即可)  2.使用者掃描了二維碼會給微信傳送資訊,然後微信把資訊以XML格式傳送給我們的伺服器 3.接收資料,並把資料保存於資料庫或者快取,程式碼如下: $wechatObj = new

蒙特.卡羅方法求解圓周率近似值原理程式碼實現

原理 對於某些不能精確求解的問題,蒙特.卡羅方法是一種非常巧妙的尋找近似解的方法。 以求解圓周率的問題為例,假設有一個單位圓及其外切正方形,我們往正方形內扔飛鏢,當扔的次數足夠多以後,“落在圓內的次數/落在正方形內的次數”這個比值會無限接近“圓的面積/

大資料教程(8.2)wordcount程式原理程式碼實現/執行

        上一篇部落格分享了mapreduce的程式設計思想,本節博主將帶小夥伴們瞭解wordcount程式的原理和程式碼實現/執行細節。通過本節可以對mapreduce程式有一個大概的認識,其實hadoop中的map、reduce程

【機器學習】Apriori演算法——原理程式碼實現(Python版)

Apriopri演算法 Apriori演算法在資料探勘中應用較為廣泛,常用來挖掘屬性與結果之間的相關程度。對於這種尋找資料內部關聯關係的做法,我們稱之為:關聯分析或者關聯規則學習。而Apriori演算法就是其中非常著名的演算法之一。關聯分析,主要是通過演算法在大規模資料集中尋找頻繁項集和關聯規則。

G711編碼原理程式碼

G711編碼的聲音清晰度好,語音自然度高,但壓縮效率低,資料量大常在32Kbps以上。常用於電話語音(推薦使用64Kbps),sampling rate為8K,壓縮率為2,即把S16格式的資料壓縮為8bit,分為a-law和u-law。 a-law也叫g711a,輸

java:集合框架(TreeSet保證元素唯一和比較器排序的原理程式碼實現)

* A:案例演示     * TreeSet保證元素唯一和比較器排序的原理及程式碼實現 按照字串長度排序 重寫了Comparator介面中的方法 class CompareByLen implem

(四)DFS檔案操作的原理程式碼實現

1、檔案操作原理 1.1、下載過程 Client向namenode發起Open file 請求。目的是獲取指定檔案的輸入流 namenode收到請求之後,會檢查路徑的合法性,客戶端的操作許可權。如果檢測未通過,則直接報錯返回 Client也會向namenode發起Get

AVL樹的原理程式碼實現

前言: 如果你還沒有學習過二叉查詢樹,那麼請你先去看看二叉查詢樹,因為AVL樹便是從二叉查詢樹進化而來的,不看的話你無法理解AVL樹。 如果你已經學習了二叉查詢樹,你會覺得二叉查詢樹效能在各方面都很好,就只有一丟丟的小毛病,那就是當資料非常坑時,二叉查詢樹退化成了一條

揹包九講--多重揹包的原理程式碼實現

本文節選這篇部落格:http://blog.csdn.net/tinyguyyy/article/details/51203935 這篇文章的內容RT 個人認為01揹包和完全揹包揹包九講講的很具體了,多重揹包關於二進位制思想的我的沒有接觸過,有點不求甚解,所

G.711編碼原理程式碼

最近看語音編碼,發現網上大都只給出了G711的程式碼,確沒有介紹原理,儘管很簡單,但直接看程式碼也是有點摸不著。下面找到了原理進行簡要的敘述,並給出了在網上找到的程式碼。 1.介紹: G.711 也稱為PCM(脈衝編碼調製),是國際電信聯盟訂定出來的一套語音壓縮標準,主要

MLP多層感知機(人工神經網路)原理程式碼實現

一、多層感知機(MLP)原理簡介多層感知機(MLP,Multilayer Perceptron)也叫人工神經網路(ANN,Artificial Neural Network),除了輸入輸出層,它中間可以有多個隱層,最簡單的MLP只含一個隱層,即三層的結構,如下圖:從上圖可以看

[轉]Base64加密原理程式碼實現

Base64是一種加料置位加密法,那為什麼叫base64呢?因為無論明文是什麼(比如漢字,特殊符號等),加密後的密文都只會變成字母A-Z、a-z和0-9 和+和/這64個字元,被他加密體後積一般會變成原來的4/3。 Base64的標準在RFC2045裡的24頁可以看到。

BatchNormalization 原理程式碼實現

原理講解 本次所講的內容為Batch Normalization,簡稱BN,來源於《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate

ssd原理程式碼實現詳解

通過https://github.com/amdegroot/ssd.pytorch,結合論文https://arxiv.org/abs/1512.02325來理解ssd. ssd由三部分組成: base extra predict base原論文裡用的是vgg16去掉全連線層. base + extr

布隆過濾器(Bloom Filters)的原理程式碼實現(Python + Java)

本文介紹了布隆過濾器的概念及變體,這種描述非常適合程式碼模擬實現。重點在於標準布隆過濾器和計算布隆過濾器,其他的大都在此基礎上優化。文末附上了標準布隆過濾器和計算布隆過濾器的程式碼實現(Java版和Python版) 本文內容皆來自 《Foundations of Computers Systems Rese

BASE64編碼原理分析指令碼實現逆向案例

BASE64編碼原理分析指令碼實現及逆向案例 0x01 簡單介紹 資料傳送時並不支援所有的字元,很多時候只支援可見字元的傳送。但是資料傳送不可能只傳送可見字元為解決這個問題就誕生了base64編碼。base64編碼將所有待編碼字元轉換成64個可見字元表中的字元。 0x02 編碼原理 被

【原創】大資料基礎之Spark(5)Shuffle實現原理程式碼解析

一 簡介 Shuffle,簡而言之,就是對資料進行重新分割槽,其中會涉及大量的網路io和磁碟io,為什麼需要shuffle,以詞頻統計reduceByKey過程為例, serverA:partition1: (hello, 1), (word, 1)serverB:partition2: (hell

sift演算法特徵描述子構建程式碼實現--梯度直方圖生成原理程式碼

0.引言 sift針對區域性特徵進行特徵提取,在尺度空間尋找極值點,提取位置,尺度,旋轉不變數,生成特徵描述子。 總共分四個步驟: step3 生成梯度直方圖 生成特徵點的梯度資訊,並且確定主方向和輔助主方向的關鍵點。 3.1 梯度計算