以文本方式查看主题

-  W3CHINA.ORG讨论区 - 语义网·描述逻辑·本体·RDF·OWL  (http://bbs.xml.org.cn/index.asp)
--  『 XSL/XSLT/XSL-FO/CSS 』  (http://bbs.xml.org.cn/list.asp?boardid=8)
----  [原创]BASE64编解码之XSLT1.0实现 - Encoding&Decoding base64 with XSLT1.0 by Qr  (http://bbs.xml.org.cn/dispbbs.asp?boardid=8&rootid=&id=75805)


--  作者:Qr
--  发布时间:6/29/2009 6:22:00 PM

--  [原创]BASE64编解码之XSLT1.0实现 - Encoding&Decoding base64 with XSLT1.0 by Qr
最近一直在研究BASE64编解码,偶然想到用XSLT来实现,在网络上搜索了半天,没有找到一篇相关的文章和说明。偶在O'REILLY的网站上看到有人求解,只见答复XSLT1.0无解,XSLT2.0才有相关方法函数,然而XSLT2.0的应用还不是很普遍。

熟悉XSLT&XPath的你一定知道,两者都没有相关方法函数可以取字符的ASCII值。而这一功能正是BASE64编解码的基本要素之一。

因为取不到ASCII值,用XSLT来实现BASE64编解码的想法几乎要放弃了。难道就这么放弃?这不是我的风格,我喜欢迎难而进!

受BASE64解码思想和之前自己用XSLT&XPath写的循环输出A-Z字符代码的启发,借助XPath中的几个字符串处理函数,终于实现了XSLT1.0环境下BASE64的编解码。

BASE64编码的原理就不再重复了,这里只附上wikipedia的BASE64编码的原理,同时介绍如何通过XSLT&XPath实现字符到“ASCII值”的转换原理:

基本的ASCII字符对应的ASCII为0-127,其中0-31、127共33个字符为控制字符,不可显示,32-126为可显示字符。据此,我们可以建立一个映射表,即“ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~,”(注意:控制字符因为无法显示, 这里暂不考虑),其在映射表中的次序加上31即得到“ASCII值”。

附wikipedia的BASE64编码的原理:

转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲区中剩下的Bit用0补足。然后,每次取出6个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。
如果最后剩下两个输入数据,在编码结果后加1个「=」;如果最后剩下一个输入数据,编码结果后加2个「=」;如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。


BASE64的解码实为编码的逆过程。

XSLT1.0实现BASE64编解码:

<?xml version="1.0" encoding="gb2312" ?>

<!--Encoding&Decoding base64 with XSLT1.0-->
<!--BASE64编解码之XSLT1.0实现-->
<!--Author:Qr http://Qr.blogger.org.cn-->
<!--Date:2009/06/29-->

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="gb2312" indent="yes"/>
<xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"/>
<xsl:output doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

<xsl:variable name="CODEPAD" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'"/>
<xsl:variable name="ASCII"><![CDATA[ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~,]]></xsl:variable>

<!--原始字符串-->
<xsl:variable name="CHAR" select="'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.'"/>
<!--原始字符串长度-->
<xsl:param name="STRLENGTH" select="string-length($CHAR)"/>
<!--循环次数-->
<xsl:param name="CYCLE_CNT" select="floor($STRLENGTH div 3)"/>
<!--模值-->
<xsl:param name="MOD" select="$STRLENGTH mod 3"/>

<!--待解码字符串-->
<xsl:variable name="STR" select="'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4='"/>
<!--待解码字符串长度-->
<xsl:param name="STRLEN" select="string-length($STR)"/>
<!--循环次数-->
<xsl:param name="CYCLECNT" select="floor($STRLEN div 4)"/>

<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="gb2312" lang="gb2312">
    <head>
        <title>BASE64编解码之XSLT1.0实现 - Encoding&amp;Decoding base64 with XSLT1.0 by Qr</title>
        <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
    </head>
    <body>
        <div style="margin:50px 168px;">
            <h1 style="text-align:center;">BASE64编解码之XSLT1.0实现</h1>
            <xsl:text>原始字符串:[Thomas Hobbes's Leviathan文句]</xsl:text>
            <textarea rows="6" cols="80" style="width:100%">
                <xsl:value-of select="$CHAR"/>
            </textarea>
            <xsl:text>编码字符串:</xsl:text>
            <textarea rows="6" cols="80" style="width:100%">
                <xsl:call-template name="base64_encode">
                    <xsl:with-param name="number" select="1"/>
                </xsl:call-template>

                <xsl:if test="$MOD != 0">

                    <xsl:value-of select="substring($CODEPAD,floor((string-length(substring-before($ASCII,substring($CHAR,$CYCLE_CNT*3+1,1)))+1+31) div 4)+1,1)"/>

                    <xsl:variable name="sec">
                        <xsl:choose>
                        <xsl:when test="1=$MOD">0</xsl:when>
                        <xsl:otherwise><xsl:value-of select="floor((string-length(substring-before($ASCII,substring($CHAR,$CYCLE_CNT*3+2,1)))+31) div 16)"/></xsl:otherwise>
                        </xsl:choose>
                    </xsl:variable>
                    <xsl:value-of select="substring($CODEPAD,((string-length(substring-before($ASCII,substring($CHAR,$CYCLE_CNT*3+1,1)))+1+31) mod 4)*16+$sec+1,1)"/>

                    <xsl:choose>
                    <xsl:when test="1=$MOD"><xsl:value-of select="'='"/></xsl:when>
                    <xsl:otherwise><xsl:value-of select="substring($CODEPAD,((string-length(substring-before($ASCII,substring($CHAR,$CYCLE_CNT*3+2,1)))+1+31) mod 16)*4+1,1)"/></xsl:otherwise>
                    </xsl:choose>

                    <xsl:value-of select="'='"/>
                     
                </xsl:if>

            </textarea>
            <xsl:text>解码字符串:</xsl:text>
            <textarea rows="6" cols="80" style="width:100%">

                <xsl:call-template name="base64_decode">
                    <xsl:with-param name="number" select="1"/>
                </xsl:call-template>

                <xsl:value-of select="substring($ASCII,(((string-length(substring-before($CODEPAD,substring($STR,$CYCLECNT * 4 - 3,1)))+1) mod 256 - 1)*4) + floor(((string-length(substring-before($CODEPAD,substring($STR,$CYCLECNT * 4 - 2,1)))) mod 256) div 16)-31,1)"/>
                <xsl:choose>
                <!--第三、四个字符为“=”-->
                <xsl:when test="substring($STR,$CYCLECNT * 4 - 1,1)='='"></xsl:when>
                <xsl:otherwise>
                    <!--第三个字符不为“=”-->
                    <xsl:value-of select="substring($ASCII,(((string-length(substring-before($CODEPAD,substring($STR,$CYCLECNT * 4 - 2,1)))) mod 16)*16 - 0) + floor(((string-length(substring-before($CODEPAD,substring($STR,$CYCLECNT * 4 - 1,1)))+1) mod 256) div 4) -31,1)"/>
                    <xsl:choose>
                    <!--全无“=”-->
                    <xsl:when test="substring($STR,$CYCLECNT * 4,1)!='='">
                        <xsl:value-of select="substring($ASCII,(((string-length(substring-before($CODEPAD,substring($STR,$CYCLECNT * 4 - 1,1)))) mod 4)*64 - 1) + floor(((string-length(substring-before($CODEPAD,substring($STR,$CYCLECNT * 4,1)))+1) mod 255)) - 31,1)"/>
                    </xsl:when>
                    <!--末字符“=”-->
                    <xsl:otherwise>
                    </xsl:otherwise>
                    </xsl:choose>
                </xsl:otherwise>
                </xsl:choose>

            </textarea>
        </div>
    </body>
</html>
</xsl:template>

<!--Encoding&Decoding base64 with XSLT1.0-->
<!--Author:Qr http://Qr.blogger.org.cn-->

<xsl:template name="base64_encode">
    <xsl:param name="number"/>
    <xsl:if test="not($number &gt; ($STRLENGTH - $MOD))">

        <!--3转4字节第1字符-->
        <xsl:value-of select="substring($CODEPAD,floor((string-length(substring-before($ASCII,substring($CHAR,$number,1)))+1+31) div 4)+1,1)"/>

        <!--3转4字节第2字符-->
        <xsl:value-of select="substring($CODEPAD,((string-length(substring-before($ASCII,substring($CHAR,$number,1)))+1+31) mod 4)*16+floor((string-length(substring-before($ASCII,substring($CHAR,$number+1,1)))+1+31) div 16)+1,1)"/>

        <!--3转4字节第3字符-->
        <xsl:value-of select="substring($CODEPAD,((string-length(substring-before($ASCII,substring($CHAR,$number+1,1)))+1+31) mod 16)*4 + floor((string-length(substring-before($ASCII,substring($CHAR,$number+2,1)))+31) div 64)+1,1)"/>

        <!--3转4字节第4字符-->
        <xsl:value-of select="substring($CODEPAD,((string-length(substring-before($ASCII,substring($CHAR,$number+2,1)))+1+31) mod 64)+1,1)"/>

        <!--根据base64的编码规则,每76个字符需要一个换行。根据实际需要取舍-->
        <!--Firefox未通过。-->
        <!--number始于1,故只须+2,而不必+3-->
        <xsl:if test="0=(($number+2) mod 19)"> </xsl:if>

        <xsl:call-template name="base64_encode">
            <xsl:with-param name="number" select="$number + 3"/>
        </xsl:call-template>

    </xsl:if>

</xsl:template>

<!--Encoding&Decoding base64 with XSLT1.0-->
<!--Author:Qr http://Qr.blogger.org.cn-->

<xsl:template name="base64_decode">

    <xsl:param name="number"/>
    <xsl:if test="$number &lt; ($STRLEN - 4)">
        <!--4转3字节第1字符-->
        <xsl:value-of select="substring($ASCII,(((string-length(substring-before($CODEPAD,substring($STR,$number,1)))+1) mod 256 - 1)*4) + floor(((string-length(substring-before($CODEPAD,substring($STR,$number+1,1)))) mod 256) div 16)-31,1)"/>
        <!--4转3字节第2字符-->
        <xsl:value-of select="substring($ASCII,(((string-length(substring-before($CODEPAD,substring($STR,$number+1,1)))) mod 16)*16 - 0) + floor(((string-length(substring-before($CODEPAD,substring($STR,$number+2,1)))+1) mod 256) div 4)-31,1)"/>
        <!--4转3字节第3字符-->
        <xsl:value-of select="substring($ASCII,(((string-length(substring-before($CODEPAD,substring($STR,$number+2,1)))) mod 4)*64 - 1) + floor(((string-length(substring-before($CODEPAD,substring($STR,$number+3,1)))+1) mod 255))-31,1)"/>
        <xsl:call-template name="base64_decode">
            <xsl:with-param name="number" select="$number + 4"/>
        </xsl:call-template>
    </xsl:if>
         
</xsl:template>

<!--Encoding&Decoding base64 with XSLT1.0-->
<!--Author:Qr http://Qr.blogger.org.cn-->

</xsl:stylesheet>

编解码例句这里还是采用Thomas Hobbes's Leviathan文句:

Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.

编码结果:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=

以上结果按base64的编码规则76字符换行,解除码时,须将换行相关字符去除再转换,但以上代码没有把这个问题考虑进去,只是将除去换行相关字符的待解码串直接赋值给变量STR。

解码结果:

Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.

由于映射表中无法将ASCII的控制字符(不可显示字符)加入,因此,待编码字符串只能包含ASCII值小于32和等于127的字符(故TAB和换行等均无法正确编解码),否则,编解码将无法顺利进行,同时也得不到正确的结果。大家如果有兴趣,可以来完善这个功能。

熟悉XSLT&XPath的你一定知道,字符处理并不是XSLT的强项,这项工作本也不该XSLT来做。本文之所以用XSLT来,纯粹学术讨论。

最后,说明一下,编解码过程中,如果把编解码后的字符串组联合起来作为变量再次递归恐怕会影响编解码的速度,所以本例将每组(3转4或4转3)编解码结果直接输出到textarea中,而没有将结果作为变量再次传入命名模板中递归。

本文系Qr[http://Qr.blogger.org.cn]原创,最初发表于:http://blogger.org.cn/blog/more.asp?name=Qr&id=46314



--  作者:hexun831012
--  发布时间:6/30/2009 5:20:00 PM

--  
汗颜
这是文本,有什么应用?不存在加密的可能。
要是图片,那就好了,可惜~~~呵呵呵。

精神可嘉,赞一个!


--  作者:Qr
--  发布时间:6/30/2009 6:05:00 PM

--  
这个基本上没什么价值。ASCII码值在32以下都不便处理,更何况图片这种需要硬编解码了。

这段时间研究BASE64比较深,写代码也花不了几个时间,网上也有人问,就随便试一下咯。


--  作者:hexun831012
--  发布时间:7/1/2009 6:02:00 PM

--  
下一课题,XSL实现MD5,活活
--  作者:Qr
--  发布时间:7/2/2009 8:00:00 PM

--  
MD5?
这个课题还是留给你去实现吧。
--  作者:kinogam
--  发布时间:7/3/2009 11:02:00 AM

--  
现在都转回这里了啊……话说,我觉得直接找个支持xslt2.0的类或者工具来开发算了
--  作者:hexun831012
--  发布时间:7/3/2009 11:30:00 AM

--  
以下是引用kinogam在2009-7-3 11:02:00的发言:
现在都转回这里了啊……话说,我觉得直接找个支持xslt2.0的类或者工具来开发算了


原来标准也能任意开发的
看来你还没达到QR的境界,呵呵
--  作者:Qr
--  发布时间:7/3/2009 8:57:00 PM

--  
以下是引用kinogam在2009-7-3 11:02:00的发言:
现在都转回这里了啊……话说,我觉得直接找个支持xslt2.0的类或者工具来开发算了

javascript版本的BASE64我已经按不同的原理写了两个版本,而xslt2.0太直接了,觉得不够过瘾,非得用1.0才够劲


kinogam兄 javascript 水平高,哪天用正则实现BASE64编解码才让人大跌眼镜呢。


--  作者:hexun831012
--  发布时间:7/3/2009 9:02:00 PM

--  
我更喜欢1.1版本,在超空间文档上作了加强,可惜并没有实现。
个人不喜欢2.0,违背了1.0的设计初衷
强化了数据处理功能,一方面降低了性能,另一方面违反设计原则

至于QR的心态,令我羡慕,玩本身是一种多大境界

我要买车了,在澳洲的日子终于不用太苦了


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
105.469ms