level 8
先占个楼,具体实现是依靠GDI+的DrawDiverString,也就是传说中的底层画法。
二楼上链接。
2015年11月23日 17点11分
1
level 8
源码是我自己用来做实验的,除了一些关键的地方使用代码格式,其他地方都很乱,其中有一个rect加了常量0.5,这是用来应付浮点误差的,没有特定含义。
应付浮点误差是为了以后做光标选中字符时,中间不出现因为浮点造成的间断。在中英文混杂的时候,浮点误差比较明显,所以要多出一点盖掉。
2015年11月23日 17点11分
3
@vbadvisor 笑炸,忘记告诉大家了,这个是调用驱动的底层画法,不能设断点中断,否则会死机。。。在自己死机过n次,后来忘记了这茬
2015年11月24日 03点11分
level 8
在复杂的文字处理程序中,往往一个字节的流水线不够用,那么完全可以用INT流水线,或者LONG流水线,甚至多个流水线并行处理。
只是增加了几十几百KB的内存花销,就可以省去大量的判断代码,以及获得相当快的处理速度,这是非常划算的。
流水线的flag太多太杂,或者有多个流水线的时候,就需要另外做一个流水线管理工具,用来录入删除编辑——其实也就是另类的数据库了,但是这个数据库是私人定制的,通过数组方式存储。通过数组下标来读取,速度十分的快。
内存读取数组是这样的,先读取数组名,数组名就是一个指针,指向了数组下标下界开始的地方,一般下标(0)的地方。然后通过数组下标的偏移,也就是指针+(下标*数组每个元素字节大小)的地址,读取数据。
直接操作底层,这比数据库快了不知道多少倍。
如果是byte数组,速度更快,直接就是指针+下标的地址,相当于C语言用指针直接读取了。
一般来说,流水线的使用方式,我推荐使用多流水线组合,主要流水线用byte数组,当遇到关键词(需要十分复杂的判断)时,再进入复杂的流水线数组里进行判断。
这样对于非关键词,可以拥有十分快速的处理速度。
在庞大复杂的文字处理中,还可以这样,7位为7号流水线,6位为6号流水线,这样的方法……
2015年11月24日 15点11分
15
level 8
上面说了string的方法,string对象是安全的,也是笨拙的,因为它不是基本数据类型,而是被VB封装起来的一个类。string其实就是包含了一个byte数组成员的类结构,比byte数组调用多出了几步开销。
在VB6里,string能做到的事情,byte数组一样能做到,甚至很多string类的函数byte数组都可以直接套用——不信大家可以做个试验。
用byte数组做为参数,可以得到更高的性能和效果,下面就来详细说一下如何使用byte数组。
首先一定要确认byte数组是双字节的,也就是unicode16编码,16就是16位,也就是一个integer的大小,2个字节。这种编码方式被称为DBCS,也就是双字节编码字符。
简单的说,每个字符都包含了两个字节。
在ascii编码中,0到127的编码都是一个字节,这其中包含回车空格等等控制符,也包含英文字母、数字、英文标点等等国际通用符号。
一般的文本存储模式是ANSI,通常来说,也就是ASCII+系统自带的另一套编码(和地区相关)。
这样的文字存储在BYTE数组里,每个字符都是不定大小的。
举个例子:65 64 32 49 13 10 176 161 (中文系统)
这个字符串就是:“A@(空格)1(回车)(换行)啊”
其中的回车换行连起来是一个完整的操作符,回车单独使用不起效果,换行倒是可以直接换,但是这并非标准的硬回车换行。通用的标准是13后加10。
176 161这个编码就是中文的啊字编码,因为中文汉字太多,一个字节容纳不下,所以需要两个字节。
这样就给使用带来了混乱,一个是单字节,一个是双字节,这样计算字符数都很麻烦,难道还要每个字节都判断是否小于127?
再看看unicode16里,同样一个字符串是怎么表示的呢?
它需要进行一个单字节转双字节的动作,所有在0到127以内的国际通用ASCII编码,通通后面加个0字节。
也就是这样:65 0 64 0 32 0 49 0 13 0 10 0 74 85
也是同样的一个字符串,只不过这次,所有小于127的字符都保持数字不变,并且后面加了一个0,汉字编码却变了。
由原来的176 161变成了74 85。
在这里,有必要说一下VB里的ASC()函数和CHR()函数。
我们可以看到asc("啊")会等于【-20319】,这个数字怎么来的呢?
我们首先进行一个简单的计算。【-20319】+【65536】=45217
【45217】除以【256】等于176.628……
【45217】MOD【256】等于161。
看到了吗?
176,161
除以256,256就是一个字节的最大容量,得到的数字是这个双字节的高字节数值,多出来的小数点0.628等等等就是第二个字节,你把0.628……去乘以256,也会得到161。但是MOD计算更快,这是求余专用的。
我们就知道了,176是高字节,161是低字节。
等等,那个-20319是怎么回事,为什么要加65536?
很简单……记得我们前面说过使用字节位flag的时候,不要使用首位吗?
因为首字节,是代表符号位的。
byte没有符号位的说法,在VB里,一个byte都是无符号的数字,你可以直接将该byte 给print出来,会是0到255区间的一个正整数。
但是在integer,long等等双字节,多字节数据类型中,首位,也就是最高位,都是符号位。
integer的16位就是符号位,long的32位就是符号位。
拿integer来举例。
0000 0000 0000 0000
这是一个正整数0。
0000 0000 0000 0001
这是1。
0111 1111 1111 1111
这是 32767,也就是integer的最大值。
1000 0000 0000 0000
这是-32768,也就是integer的最小值。
1111 1111 1111 1110
这是-2。
1111 1111 1111 1111
这是-1。
首位被当作了符号位,那么双字节输出成数值时,就会看到这样的效果。
汉字“啊”的GB编码的二进制表示如下:
1011 0000 1010 0001
首位是1,所以输出的时候,会变成一个负数,也就是把1011的1当作-符号,把011 0000 1010 0001全部取反,也就是0100 1111 0101 1110,这是正整数20318,再和符号位一起变成-,同时减1,便成为了-20319。
减1是因为数值0占位,也就是说,0到正整数20318,一共有20319个,相反就是-20319,因为没有-0的说法,所以0就让给了正整数,使得负整数多出一个。
这也就是integer在-32768 和32767区间的本质,负数多一个,大家也都明白了原因。
明白这个有吊用?
当然有……
汉字的编码本质上是一个正整数,只是算作数值的时候,变成了一个负数。
那么我们前面说的流水线,就不能简单的0到65535。顺便提一下,65535的由来,是一个字节0到255,两个字节就是0到255*255,也就是65535,这是双字节编码的区间。
也要改变一下,下标下界变成-32768,上界为32767。
这样就能和转换后的数值对应的上,可以通过该数值直接读取下标,而不用进行+65535的计算。
回到unicode上来,毕竟我们在VB里操作,都是用unicode,更方便操作。
176 161的啊字,在unicode里显示为74 85。
按照前面的算法反推,176 161只要将高字节176*256,再加上余数161,就可以得到字符的编码45217,转换成负数,就是-20319。
同理,74*256+85等于19029。这个不是负数,所以就是该字符的编码。
对吗?
等等,先看看ascw("啊")再说,什么,ascw("啊")竟然等于21834?
算错了?哪里出问题?
先别急,先把21834解析一下,照样除以256,得到85.289……
MOD 256,得到……74!
85,74……对应74,85
反了?
这是怎么回事?
原理很简单,unicode编码,有两种形式,一种是大端法,一种是小端法。
大端法就是85,74的形式显示,小端法就是74,85的形式显示。
大多数情况下,unicode都是小端法表示。
为什么我要了解这些没用的玩意?不,其实很有用,马上就用到了。
2015年11月25日 04点11分
19
level 8
回到byte数组上,我们如何提取其中的一个字符,来进行判断呢?
如果是string,我们知道可以有mid$函数,有ASCW函数等等,数组你怎么搞?
数组其实更简单。
假设byte()是以下标(0)开始的数组,并假设byte数组已经做好了unicode转换,是可以直接用的字节数组。
那么,当i为偶数时,byte(i),byte(i+1)就是一个字符。
i=0
byte(0),byte(1)就是一个字符。比如说空格字符,byte(0)=32,byte(1)=0
按照unicode的小端法表示,我们可以知道空格字符的编码,是 byte(1)*256+byte(0),也就是靠后的是更大的,0
+3
2,还是32,这和ASCII兼容,没有错。
假如是大端法的unicode,那么空格字符的表示,就应该为byte(0)=0,byte(1)=32。也就是0,32
怎么做,把它提取出来。
很简单,这里我们使用一个API——别看是API,但这是操作内存的方法,速度超级快。
dim itemp as integer
CopyMemory byval varptr(itemp),byval varptr(b(i)),2
varptr(b(i)),是指向b(i)的指针,按照上面的方法,i是偶数。byval是传值。
翻译过来,就是 指向b(i)的地址,要传值出来。
传值到哪里去?
指向itemp的地方,也是byval传值进去到内存里。
传多少字节?
2个字节。
itemp是一个integer变量。
我们在这里直接把字符传给正整数,能不能行得通?会不会爆炸?
在前面说过,integer其实就是一个byte(1)的固定大小字节数组……所以大可放心。
我们print一下itemp。
itemp=21834
碉堡了,没有chrw,也没有mid,没有任何乘以除以MOD计算,直接一个复制内存,我们就得到了想要的结果。
用byte数组,直接操作内存,就是这么愉快。
现在可以看得处理字符的大框架了。
dim itemp as integer
for i=0 to ubound(b) step 2
'step 2是因为双字节,不用一个个的录入,直接跳着录就行了。
CopyMemory byval varptr(itemp),byval varptr(b(i)),2
if 流水线byte(itemp) then
'进入流水线
else
'无flag,进行默认操作
endif
next i '下一轮
2015年11月25日 04点11分
20