ASP/VBScript模拟实现PHP extract()函数将字典集合转换为对象

写过PHP的都知道,其有个extract()非常方便,可以便捷的将字典转换为变量,当然到ASP中则要受限很多,特别是VBScript脚本,本文叙述的就是一种转换的思路,可以实现类似的功能。

首先需要我之前公布的一个类DynamicObject(最新版本的),大家可以移步这里找到相应的代码和说明。

了解了DynamicObject类和其工作原理后,下面我就直接提供ASP版本的extract代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
'
' ASP/VBScript Dictionary extract
' Author: WangYe
' For more information please visit
'     //wangye.org/
' This code is distributed under the BSD license
'
' collection 集合或者字典,可以通过For Each访问的
'            Request.Form 或者 Request.QueryString
' specified  指定必须存在的属性,假如该属性不存在,将自动创建一个
' prefix     每个属性的前缀修饰
' callback   对于集合或者字典的每个元素(key-value)的值进行函数调用
'            函数原型:
'            Function filter(key, value)
'                filter = value
'            End If
'            最终值将以该函数返回的值为准
' 
Function extract(collection, ByVal specified, prefix, callback)
    Dim VarName, VarValue, DynObj, searchKey
    specified = "," & Replace(specified, " ", "") & ","
 
    Set DynObj = New DynamicObject
    For Each key In collection
        searchKey = "," & key & ","
        If InStr(1, specified, searchKey, 1)>0 Then
            specified = Replace(specified, searchKey, "")
            If Left(specified, 1) <> "," Then
                specified = "," & specified
            End If
            If Right(specified, 1) <> "," Then
                specified = specified & ","
            End If
        End If
 
        VarName = prefix & key
 
        VarValue = collection(key)
        If callback<>"" Then
            VarValue = GetRef(callback)(key, VarValue)
        End If
 
        DynObj.add VarName, VarValue, PROPERTY_ACCESS_READONLY
	Next
 
    specified_array = Split(specified, ",")
    Dim i
    For i = LBound(specified_array) To UBound(specified_array)
        If specified_array(i)<>"" Then
            DynObj.add prefix & specified_array(i), "", _
		PROPERTY_ACCESS_READONLY
        End If
    Next
    Set extract = DynObj.GetObject()
End Function

继续阅读

ASP/JScript将字典对象(Scripting.Dictionary)存储到Application

最近要改进一个旧项目,项目采用ASP(JScript)编写,由于时间问题一直没有重构,所以还是ASP将就着,考虑到效率问题,准备把部分内容缓存起来,当然直接读写Application缓存不是很妥当的,遂采取缓存字典对象(Scripting.Dictionary)来实现。好了,一般会认为代码如下所示:

var key = "dict_cache"
var obj = Server.CreateObject("Scripting.Dictionary");
 
Application.Lock();
Application.Contents(key) = obj;
Application.Unlock();

偏偏事与愿违,如果你这样做的话,IIS会报“不允许的对象使用方式”错误,具体内容如下:

Application 对象 错误 'ASP 0197 : 80004005' 

不允许的对象使用方式 

/index.asp,行 12 

不能将具有单元模型行为的对象添加到应用程序的内部对象中。 

继续阅读

Python的字典(Dictionary)按顺序遍历

最近尝试着改用Python取代VBScript来实现一些功能,当然也遇到了一些麻烦,这里我要说明的是Python的字典和VBScript所调用的字典对象是有些区别的。

字典可以叫做哈希(Hash)表或者K-V存储表(Key-Value),也就是说一个独立的键值对应一条数据,Key是不能重复的,因为需要快速索引数据,字典进行数据查找,效率是很高的,当然其效率是靠空间换来的。

好了,说了这么多,下面介绍下,我遇到的一点区别,在VBScript中不原生支持字典特性,不过我们可以通过CreateObject(“Scripting.Dictionary”)来创建一个字典对象,然后通过Add方法添加Key Value,也可以说我比较的是Python和Scripting.Dictionary组件对象的区别,我在前面的文章中简单的介绍过这个对象相关的使用方法,这里再给出相应的脚本:

继续阅读

ASP/WScript/VBScript实现ActiveX COM对象管理器(2)

上面一篇已经介绍了关于对象管理器类cObjectManager,这篇主要讲在ASP中实现持续性对象管理器,大家知道,在ASP技术中,Global.asa是一个特殊的文件,其随着用户访问网站而启用,并在整个会话期内有效,我们可以将cObjectManager字典对象Scripting.Dictionary单独分离到这个文件中,该怎么做才能让所有ASP文件都能共享这一个对象呢?其实我们只需要在Global.asa中应用<object runat=”server”></object>这样的标签就可以创建一个静态的全局对象了。

语法:

1
2
3
4
<object runat="server" scope="scope" id="id"
{progid="progID"|classid="classID"}>
....
</object>

scope 设置对象的作用域(作用范围)(Session 或者 Application)。
id 为对象指定一个唯一的 id。
ProgID 与 ClassID 关联的 id。ProgID 的格式是:[Vendor.]Component[.Version]。ProgID 或 ClassID 必需被指定。
ClassID 为 COM 类对象指定唯一的 id。ProgID 或 ClassID 必需被指定。

global.asa

1
2
<object runat="server" scope="application" id="objMgrDict"
progid="Scripting.Dictionary"></object>

然后我们只需要简单修改一下cObjectManager类:

继续阅读

ASP/WScript/VBScript实现ActiveX COM对象管理器(1)

本文仅实现了一种理论上的管理已经创建ActiveX COM方法,部分代码可以直接使用,由于未进行性能及稳定性测试,所以不建议使用到实际生产环境。下面所述将以VBScript脚本语言为例,同样适用于ASP、VB及VBA(Visual Basic For Application)技术。

大家知道在VBScript中创建对象是通过CreateObject实现的,由于是对象类型,所以必须通过Set关键字进行对象引用,当对象使用完毕后要通过Set [对象名] = Nothing进行对象销毁,这样VBS内部对象引用计数才下降,直至完全销毁回收。

大部分情况下,我们所创建的对象是可以复用的,也就是说,我们在一次使用完毕后可以不用急于销毁对象,然后第二、三次继续使用这个已经存在的对象,这样就避免多次调用CreateObject带来性能上的损耗,以及可能创建失败的风险。

比如说有这样的WScript/VBScript代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Function IsFileExists(filename)
  Dim fso
  Set fso = CreateObject("Scripting.FileSystemObject")
    IsFileExists = fso.FileExists(filename)
  Set fso = Nothing
End Function
 
Function IsFolderExists(filename)
  Dim fso
  Set fso = CreateObject("Scripting.FileSystemObject")
    IsFolderExists = fso.FolderExists(filename)
  Set fso = Nothing
End Function
 
Sub WriteTextFile(filename, text)
  Dim fso, f
  Set fso = CreateObject("Scripting.FileSystemObject")
    Set f = fso.CreateTextFile(filename, true)
      f.WriteLine text
      f.Close
    Set f = Nothing
  Set fso = Nothing
End Sub
 
If Not IsFileExists("C:\test\data.txt") Then
  If IsFolderExists("C:\test") Then
     WriteTextFile "C:\test\data.txt", "Hello world!"
  End If
End If

这里实现了一个简单的功能,判断文件data.txt,如果不存在,然后判断C:\test文件夹是否存在,存在的话就写入data.txt文件,简单的功能包含了“语法糖”般的函数调用,虽然这样做封装特性比较好,而且提升了我们编码的效率,但是我们这里创建了3次Scripting.FileSystemObject对象,因此程序执行的性能可想而知了,其实较好的做法是创建1次Scripting.FileSystemObject对象,然后FileExists、FolderExists以及CreateTextFile都属于上下文无关的方法,因此可以复用,所以有了下面2种办法:

继续阅读

Scripting.Dictionary字典对象按键名Key进行冒泡排序

最近加班比较多,代码写得有点乱,结果今天出现了个低级错误,原本想把Scripting.Dictionary对象的Item按照指定fnCompare函数作用的Key字段排序,原本以为很简单,于是就拿了个普通的冒泡排序就用了起来,结果问题就出现了,这里有问题的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Option Explicit
 
Function fnCompare(key1, key2)
 If CInt(key1)>CInt(key2) Then
   fnCompare = 1
 ElseIf CInt(key1)<CInt(key2) Then
  fnCompare = -1
 Else
  fnCompare = 0
 End If
End Function
 
Function Sort(dict)
  Dim i,j, temp
  Dim keys,items
 
  For i = 0 To  dict.Count-1
    For j = i+1 To dict.Count - 1
	  keys = dict.Keys
	  items = dict.Items
      If fnCompare(keys(i), keys(j))>0 Then
        ' 交换Item项目
        temp = items(i)
        dict.Item(keys(i)) = items(j)
        dict.Item(keys(j)) = temp        
        ' 交换Key键名
        temp = keys(i)
        dict.Key(keys(i)) = keys(j)
        dict.Key(keys(j)) = temp
      End If
    Next
  Next
End Function
 
Sub VBMain()
  Dim dict
  Set dict = WSH.CreateObject("Scripting.Dictionary")
    dict.Add "2", "a"
    dict.Add "8", "b"
    dict.Add "1", "c"
    Sort dict
  Set dict = Nothing
End Sub
 
Call VBMain()

貌似这样看上去很正常,算法没有什么问题,交换Item后交换Key,貌似也没有问题,但是偏偏运行时出现了下面这个错误框。

继续阅读

使用Scripting.Dictionary字典对象

Scripting.Dictionary是个很有用的组件,其创建了类似于Key索引对应Value值的字典对象,并且在其内部提供了快速索引访问的机制,可以让我们通过Key直接索引到指定的Value,比遍历二维数组有效得多。

其在VBScript中是这样访问的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Dim objDict
Set objDict = WSH.CreateObject("Scripting.Dictionary")
  ' .Add(key, value)    
  objDict.Add "a", "value1"
  objDict.Add "b", "value2"
  objDict.Add "c", "value3"
  '直接通过key=b索引到value2
  WSH.Echo objDict.Item("b")
  objDict.Remove "b" ' 删除索引b及其对应的值
 
  ' 以下是遍历字典
  Dim objKeys, objItems, i
  objKeys = objDict.Keys
  objItems = objDict.Items
  For i = 0 To objDict.Count -1
    WSH.Echo "Key=" & objKeys(i) &_
             " AND Value=" & objItems(i)
  Next
  ' 判断指定的key是否存在
  If objDict.Exists("b") Then
    WSH.Echo "Found it"
  Else
    WSH.Echo "Not Exists!"
  End If
  objDict.RemoveAll  ' 清空字典内所有的key及其对应value
Set objDict = Nothing

当然在JScript访问的方式一样,但是在遍历这里需要一点点变动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  var dict = WSH.CreateObject("Scripting.Dictionary");
  dict.Add("a", "value1");
  dict.Add("b", "value2");
  dict.Add("c", "value3");
  WSH.Echo(dict.Item("b"));
  dict.Remove("b");
 
  // 注意这里的遍历
  var keys = new VBArray(dict.Keys());
  var items = new VBArray(dict.Items());
 
  for (var i=0; i < dict.Count; i++) {
    WSH.Echo("Key=" + keys.getItem(i) +
             " AND Value=" + items.getItem(i));	
  }
 
  if (dict.Exists("b")) {
    WSH.Echo("Found it");
  } else {
    WSH.Echo("Not Exists!");
  }
 
  dict.RemoveAll();

由于Scripting.Dictionary的Keys和Items的集合返回的是VB安全数组,也就是说JScript访问需要多个转换的步骤,方法就是采用new VBArray()对象,相关信息可以参考MSDN的《VBArray Objects》,当获得VBArray对象后就可以通过getItem方法获取数组元素了,值得注意的是这个VBArray对象自己不创建数组,其只起到一个转换的作用,也可以将其看成是一个操作接口吧。当然其toArray()方法可以将其转换为真正的JScript数组,届时可以直接像操作JScript数组一样操作VBArray转换的对象了。