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时,如果数据流中没有前缀或者前缀被破坏,那么文件内容的前部还是会被插入前缀的。