深入解析ASP核心技术
上QQ阅读APP看书,第一时间看更新

2.3.2 文本数据

1. 写入文本数据

处理方式为文本时,即Type属性为2时,写入数据使用WriteText方法,如:

stream.WriteText "哈哈"

如果要写入行分隔符,可以这样:

stream.WriteText "哈哈" & vbCrLf

实际上,可以更简单一些:

stream.WriteText "哈哈",1

这里使用了WriteText方法的第二个参数,它的可选值是0和1,默认是0,只写数据,如果是1的话,则写入数据之后,会再写入一个行分隔符。

行分隔符默认的就是vbCrLf,可以通过Stream对象的LineSeparator属性来修改它,它的可选值如表2-8所示。

表2-8 行分隔符的可选值

2. 读取文本数据

处理方式为文本时,读取数据使用ReadText方法,它只有一个参数,是要读取字符的个数。注意是字符个数,不是字节数。如果参数省略,则从当前位置一直读到数据流的末尾。

该参数还有两个特殊值,-1和-2。前者的作用与省略参数一样,后者则表示读取一行数据。如果指针位于一行的中间,则从当前位置读到行分隔符之前。如果想跳过一行不读取,可以使用SkipLine方法。

看一下范例。

TextWriteAndRead.asp

<%
Dim stream
Set stream = Server.CreateObject("ADODB.Stream")
stream.Type = 2                     '文本方式
stream.Charset = "GBK"
stream.Open

'写入数据
stream.WriteText "哈哈",1           '写入行分隔符
stream.WriteText "Hello World" & vbCrLf
stream.WriteText "你好"

'读取所有文本
stream.Position=0
response.write "<textarea rows='5' cols='20'>"
response.write stream.ReadText
response.write "</textarea>"

'读取文本
stream.Position=0
stream.SkipLine                     '跳过第一行
response.write "<textarea rows='3' cols='20'>"
response.write stream.ReadText(-2)   '读取一行
response.write stream.ReadText(-2)   '读取一行
response.write "</textarea>"

stream.close
Set stream = nothing
%>

运行结果如图2-13所示。

为了看出换行符的作用,这里使用了<textarea>文本框来显示文字。右边文本框中的“Hello World”和“你好”是显示在一行的,说明使用参数-2读取的一行文字是不包含行分隔符的。

图2-13 文本数据写入与读取

3. 指针的移动

指针就是指向当前位置的一个东西,它指向哪里,哪里就是开始处理的位置。使用Position属性可以得到指针当前的位置,如果指针位于数据流的开头位置,则Position属性为0。

我们来看一下指针移动的示意图,如图2-14所示。

图2-14 指针移动的示意图

从图2-14可以看出,伴随着数据的读写操作,指针一直在移动。所以,读取数据之前要留意一下指针的位置,必要时,先把它移动到正确的位置,再读取数据。

处理形式为二进制时,指针的移动也是类似的。

4. Charset的作用

处理方式为文本时,可以通过Charset属性指定文本的字符集,不指定则默认的是Unicode。处理方式为二进制时,不要使用Charset属性,会报错。变更Charset,要求指针必须指向位置0,其他位置该属性是只读的。

为了理解Charset的作用,我们做一个简单的试验。

首先,先看一下getMemoryFormat方法,它的功能是取得二进制数据的字符串表示形式,后面的例子中将省略它。

Function getMemoryFormat(bstr)
    Dim result, i
    For i=1 To Lenb(bstr)
            numberHex = Hex(AscB(MidB(bstr, i,1)))
            If Len(numberHex) = 1 Then
                    numberHex = "0" & numberHex
            End If
            result = result & " " & numberHex
    Next
    getMemoryFormat = result
End Function

再看一下例子中要用到两个字符:“編碼”,没错,是两个繁体字,它们在各种字符集中的编码如表2-9所示。

表2-9 “編碼”两字的各种编码

试验程序如下,我们不输出文本,而是输出Stream对象中二进制数据的字符串表示形式。

StreamCharset.asp

<%@codepage=936%>
<! --#include File="getMemoryFormat.asp" -->
<%
Response.Charset="GBK"

Dim stream
Set stream = Server.CreateObject("ADODB.Stream")
stream.Type = 2      '文本方式
stream.Charset = "GBK"
stream.Open

'写入文本
stream.WriteText "編碼"

'变更为二进制方式,读取并输出字符串形式
stream.Position = 0
stream.Type = 1      '二进制方式
response.write getMemoryFormat(stream.Read)

stream.close
Set stream = nothing
%>

运行结果如图2-15所示。

图2-15 Charset的作用

这表明Stream对象的4个字节中保存的二进制数据是:BE 8E B4 61。这是什么?没错,正是“編碼”两个字对应的GBK的编码。

我们再将程序中的Charset依次变更为Big5、Shift_JIS、Unicode和UTF-8,运行并汇总之后,我们得到表2-10。

表2-10 不同Charset的结果比较

对比一下,可以得知:

❑Charset为GBK、Big5和Shift_JIS时,Stream中保存的就是字符对应的编码,没有前缀。

❑Charset为Unicode时,保存的是低位在前的编码,而且有两个字节的前缀:FF FE。

❑Charset为UTF-8时,有前缀EF BB BF。

再深入想一下,ASP运行时,内部变量都是以Unicode编码形式存在的,也就是说,Stream的入口数据是Unicode编码的。那么,我们可以说,向Stream对象写入数据时,进行了Unicode编码到Charset指定编码的转换。

对应地,使用ReadText方法读取文本时,也进行了Charset指定编码到Unicode编码的转换,Stream的出口数据是Unicode编码的。还有一点,数据写入之后,再变更Charset并不会影响已有的数据,只会影响之后写入的数据。

范例如下所示。

StreamCharsetChange.asp

<%@codepage=936%>
<! --#include File="getMemoryFormat.asp" -->
<%
Response.Charset="GBK"

Dim stream
Set stream = Server.CreateObject("ADODB.Stream")
stream.Type = 2             '文本方式
stream.Charset = "GBK"
stream.Open

'写入文本
stream.WriteText "編碼"

'变更Charset(指针需要移到位置0)
stream.Position = 0
stream.Charset = "UTF-8"

'再写入文本
stream.Position = 4        '跳过已有的数据,以防被覆盖
stream.WriteText "編碼"

'变更为二进制方式,读取并输出字符串形式
stream.Position = 0
stream.Type = 1             '二进制方式
response.write getMemoryFormat(stream.Read)

stream.close
Set stream = nothing
%>

运行结果如图2-16所示。

图2-16 变更Charset的影响

可以看到,变更Charset为UTF-8后,之前的4个字节并没有变化,而后写入的文字则是按UTF-8编码写入的。

5. 前缀写入的时机

Charset为Unicode或UTF-8的时候,在位置0时调用WriteText方法,它就会自动写入前缀。如Charset为UTF-8时,写入一个字符“a”后,Stream的Size是4,而不是1。

写入的前缀没有任何特殊之处,它像普通的数据一样,可以被覆盖,所以处理的时候要注意跳过前缀。但覆盖了也不要紧,把指针移到位置0再写入数据,前缀就回来了。

中途变更Charset时要留意一下前缀。Unicode和UTF-8之间变更的时候,由于都有前缀,变更之后写入的数据会覆盖之前的前缀。从二者变更为GBK、Big5或Shift_JIS等没有前缀的Charset时,前缀就遗留了下来,要留意它们的存在。

下面看一个例子,以加深理解。

StreamCharsetPrefix.asp

<%@codepage=936%>
<! --#include File="getMemoryFormat.asp" -->
<%
Response.Charset="GBK"

'输出流中的数据
Sub printStream(stream)
    Dim savePosition
    savePosition = stream.Position           '保存指针位置

    '输出数据
    stream.Position = 0
    stream.Type = 1
    response.write "流中的数据:" & getMemoryFormat(stream.read) & "<br><br>"

    stream.Position = 0
    stream.Type = 2
    stream.Position = savePosition           '恢复指针位置
End Sub

Dim stream
Set stream = Server.CreateObject("ADODB.Stream")
stream.Type = 2                              '文本方式
stream.Charset = "UTF-8"
stream.Open
stream.WriteText "哈哈"
Call printStream(stream)                     '看看流中的数据

stream.Position = 0
stream.Charset = "Unicode"                   '变更Charset为Unicode
Call printStream(stream)

stream.WriteText "a"       '在位置0写入一个字符,将自动写入前缀
Call printStream(stream)

stream.Position = 1        '在位置1写入一个字符,将覆盖前缀
stream.WriteText "b"
Call printStream(stream)

stream.Position = 0        '在位置0写入一个字符,将自动写入前缀
stream.WriteText "c"
Call printStream(stream)

stream.Close
Set stream = nothing
%>

运行结果如图2-17所示。

图2-17 Charset前缀的写入时机

还有一点需要提醒一下,以文本方式保存到文件并且Charset为Unicode或UTF-8时,如果数据流中没有前缀或者前缀被破坏,那么文件内容的前部还是会被插入前缀的。