VB - 自编字符串段值处理函数

来源:百度文库 编辑:神马文学网 时间:2024/04/29 09:46:40

>>字符串的分段定律
    对于任何非空字符串S,若以其连续单个或多个子字符串M作为分隔符,则S被M分隔的段数等于M在S中出现的个数加一,每一段段值为对应去除M后的字符串;若以其他字符串N作为分隔符,则S被N分隔的段数恒等于零,且每一段段值均为空。这就叫做字符串的分段定律。

●现举例说明该定律:
设字符串Str=2|52|622|
Str中分隔符的选定:
①.若以“|”为分隔符,则有S被“|”分隔的段数=“|”在S中出现的的个数+1=3+1=4;
   每一段段值分别为:2、52、622、[空]
②.若以“52”为分隔符,则有S被“52”分隔的段数=“52”在S中出现的个数+1=1+1=2;
   这两段段值分别为:2|、|622|
③.若以“2|52|622|”为分隔符,则有S被“2|52|622|”分隔的段数=2;
   这两段段值均为空
③.若以“╳”为分隔符,则有S被“╳”分隔的段数恒等于0。
   每一段段值均为空

而下面列出的两个版本的函数就是建立在上述分段定律的基础之上并能够进行段值处理的实用函数。

★经典版★
'------------------------------------------------{Get_nStr『经典版』}------------------------------------------------
'【函数说明】:
'取出以非空字符串中的连续单个或多个子字符串为分隔符隔开的第[n]段字符串,又名“取段”函数。
'【参数含义】:
'① [wStr] → 非空字符串;
'② [Mark] → 分隔符;
'③ [n]    → 被分隔符隔开的第[n]段字符串;
'④ [Opt] → 可选参数:是否启用“倒数第[n]”(缺省值为False,即“正数第[n]”);
'⑤ [p]    → 可选参数:被分隔符分隔的总段数,使用前必须定义相关变量(防止ByRef参数类型不符)。
'【参数范围】:
'[n] → "1 - 32767" 之间的整数(负数和零没有实际意义)。
'[p] → "0 - 32767" 之间的整数(负数没有实际意义)。
'-------------------------------------------------------------------------------------------------------------------------------
Function Get_nStr( _
ByVal wStr As String, _
ByVal Mark As String, _
ByVal n As Integer, _
Optional ByVal Opt As Boolean = False, _
Optional p As Integer) As String
    Dim i% '计数器
    Dim StartPos%, MarkPos% '分别定义:查找的起始位置、分隔符所在的位置
    Dim MarkLength%          '[Mark]的长度
   
    StartPos = 1    '搜索起点为1
    MarkLength = Len(Mark) '取得[Mark]的长度

    '╱*若[wStr]的长度为0,则退出过程*╱
    If Len(wStr) = 0 Then Exit Function

    '╱*此句可防止下面的Do...Loop Until语句中的[p]值溢出*╱
    If Mark = "" Then Exit Function

    '╱*算出字符串[wStr]中被分隔符[Mark]分隔的总段数*╱
    Do
        MarkPos = InStr(StartPos, wStr, Mark)
        p = p + 1
        StartPos = MarkPos + MarkLength
    Loop Until MarkPos = 0

    '╱*未找到分隔符时*╱
    If p = 1 Then
        p = 0
        Exit Function
    Else
        '╱*找到分隔符时且[n]为零或负*╱
        If n < 1 Then Exit Function
    End If
   
    StartPos = 1    '重置起点
   
    '╱*若[n]在总段数[p]的范围之内(包括[p])*╱
    If n <= p Then
        If Opt = True Then n = p - n + 1    '若[Opt]的值为True,则启用“倒数第[n]”
        If n <= CInt(p / 2) Then    '[n]在前半段(包括中间段)
            For i = 1 To n '循环计数器[i]至第[n]段
                MarkPos = InStr(StartPos, wStr, Mark)   '定位分隔符(从前往后)
                Get_nStr = Mid(wStr, StartPos, MarkPos - StartPos) '取出第[n]段字符串
                '设定下一搜索起点
                StartPos = MarkPos + MarkLength
            Next
        Else    '[n]在后半段(排除中间段)
            StartPos = Len(wStr)
            For i = 1 To p - n + 1 '循环计数器[i]至第[n]段
                MarkPos = InStrRev(wStr, Mark, StartPos) '定位分隔符(从后往前)
                '取出第[n]段字符串
                Get_nStr = Mid(wStr, MarkPos + MarkLength, StartPos - (MarkPos + MarkLength) + 1)
                '设定下一搜索起点
                StartPos = MarkPos - 1
            Next
        End If
    End If
End Function

'-------------------------------------------------{Set_nStr『经典版』}-------------------------------------------------
'【函数说明】:
'设定以非空字符串中的连续单个或多个子字符串为分隔符隔开的第[n]段字符串,简称“设段”函数。
'【参数含义】:
'① [OriStr] → 非空字符串;
'② [Sign]   → 分隔符;
'③ [n]      → 被分隔符隔开的第[n]段字符串;
'④ [NewStr] → 欲设定的新字符串;
'④ [Chk]    → 可选参数:是否启用“倒数第[n]”(缺省值为False,即“正数第[n]”);
'⑤ [d]      → 可选参数:被分隔符分隔的总段数,使用前必须定义相关变量(防止ByRef参数类型不符)。
'【参数范围】:
'[n] → "1 - 32767" 之间的整数(负数和零没有实际意义)。
'[d] → "0 - 32767" 之间的整数(负数没有实际意义)。
'---------------------------------------------------------------------------------------------------------------------------------
Function Set_nStr( _
ByVal OriStr As String, _
ByVal Sign As String, _
ByVal n As Integer, _
ByVal NewStr As String, _
Optional ByVal Chk As Boolean = False, _
Optional d As Integer) As String
    Dim k% '计数器
    Dim BeginPos%, SignPos%, NextSignPos%   '分别定义:搜索起点、分隔符位置、下一个分隔符位置
    Dim LStr$, RStr$    '分别定义:第[n]段以左的所有字符串、第[n]段以右的所有字符串
    Dim OriStrLength%, SignLength% '分别定义:[OriStr]的长度、[Sign]的长度
   
    BeginPos = 1    '搜索起点为1
    OriStrLength = Len(OriStr) '取得[OriStr]的长度
    SignLength = Len(Sign)      '取得[Sign]的长度
   
    '╱*若[OriStr]长度为零,则退出过程*╱
    If OriStrLength = 0 Then Exit Function
   
    '╱*此句可防止下面的Do...Loop Until语句中的[d]值溢出*╱
    If Sign = "" Then Set_nStr = OriStr: Exit Function
   
    '╱*算出字符串[OriStr]中被分隔符[Sign]分隔的总段数*╱
    Do
        SignPos = InStr(BeginPos, OriStr, Sign)
        d = d + 1
        BeginPos = SignPos + SignLength
    Loop Until SignPos = 0

    '╱*未找到分隔符时*╱
    If d = 1 Then
        d = 0
        Set_nStr = OriStr
        Exit Function
    Else
        '╱*找到分隔符时且[n]为零或负*╱
        If n < 1 Then
            Set_nStr = OriStr
            Exit Function
        End If
    End If

    BeginPos = 1    '重置起点

    '╱*[n]为首段时*╱
    If n = 1 Then
        Select Case Chk
            Case False '未启用“倒数第[n]”时处理首段
                SignPos = InStr(BeginPos, OriStr, Sign)
                RStr = Right(OriStr, OriStrLength - SignPos + 1)
                Set_nStr = NewStr & RStr    '设定首段字符串
            Case True   '启用“倒数第[n]”后转化为处理尾段(分两种情况①、②)
                If Right(OriStr, SignLength) <> Sign Then
                    For k = OriStrLength To 1 Step -1
                        If Mid(OriStr, k, SignLength) = Sign Then
                            SignPos = k
                            Exit For
                        End If
                    Next
                    LStr = Left(OriStr, SignPos + SignLength - 1)
                    Set_nStr = LStr & NewStr    '①.设定尾段字符串(无分隔符[Mark]时)
                Else
                    Set_nStr = OriStr & NewStr '②.设定尾段字符串(有分隔符[Mark]时)
                    Exit Function
                End If
            End Select

    '╱*[n]在总段数[d]的范围之内*╱
    ElseIf n < d Then
        If Chk = True Then n = d - n + 1    '若[Opt]的值为True,则启用“倒数第[n]”
        If n <= CInt(d / 2) Then    '[n]在前半段(包括中间段)
            For k = 1 To n - 1
                SignPos = InStr(BeginPos, OriStr, Sign)
                NextSignPos = InStr(SignPos + SignLength, OriStr, Sign)
                LStr = Left(OriStr, SignPos + SignLength - 1)
                RStr = Right(OriStr, OriStrLength - NextSignPos + 1)
                BeginPos = SignPos + SignLength
            Next
            Set_nStr = LStr & NewStr & RStr '设定中间任意段字符串
        Else    '[n]在后半段(排除中间段)
            BeginPos = OriStrLength '以尾字符串为搜索起点
            For k = 1 To d - n
                SignPos = InStrRev(OriStr, Sign, BeginPos)
                NextSignPos = InStrRev(OriStr, Sign, SignPos - 1)
                LStr = Left(OriStr, NextSignPos + SignLength - 1)
                RStr = Right(OriStr, OriStrLength - SignPos + 1)
                BeginPos = SignPos - 1
            Next
            Set_nStr = LStr & NewStr & RStr '设定中间任意段字符串
        End If

    '╱*[n]为尾段时*╱
    ElseIf n = d Then
        Select Case Chk
            Case False '未启用“倒数第[n]”时处理尾段(分两种情况①、②)
                If Right(OriStr, SignLength) <> Sign Then
                    For k = OriStrLength To 1 Step -1
                        If Mid(OriStr, k, SignLength) = Sign Then
                            SignPos = k
                            Exit For
                        End If
                    Next
                    LStr = Left(OriStr, SignPos + SignLength - 1)
                    Set_nStr = LStr & NewStr    '①.设定尾段字符串(无分隔符[Mark]时)
                Else
                    Set_nStr = OriStr & NewStr '②.设定尾段字符串(有分隔符[Mark]时)
                    Exit Function
                End If
            Case True   '启用“倒数第[n]”后转化为处理首段
                SignPos = InStr(BeginPos, OriStr, Sign)
                RStr = Right(OriStr, OriStrLength - SignPos + 1)
                Set_nStr = NewStr & RStr    '设定首段字符串
            End Select

    '╱*[n]超出总段数时*╱
    Else
        Set_nStr = OriStr   '设定超出段字符串
    End If
End Function

★『现代版』★
'------------------------------------------------{Get_nStr『现代版』}------------------------------------------------
'【函数说明】:
'取出以非空字符串中的连续单个或多个子字符串为分隔符隔开的第[n]段字符串,又名“取段”函数。
'【参数含义】:
'① [wStr] → 非空字符串;
'② [Mark] → 分隔符;
'③ [n]    → 被分隔符隔开的第[n]段字符串;
'④ [Chk] → 可选参数:是否启用“倒数第[n]”(缺省值为False,即“正数第[n]”);
'⑤ [d]    → 可选参数:被分隔符分隔的总段数,使用前必须定义相关变量(防止ByRef参数类型不符)。
'【参数范围】:
'[n] → "1 - 32767" 之间的整数(负数和零没有实际意义)。
'[d] → "0 - 32767" 之间的整数(负数没有实际意义)。
'-------------------------------------------------------------------------------------------------------------------------------
Function Get_nStr( _
ByVal wStr As String, _
ByVal Mark As String, _
ByVal n As Integer, _
Optional ByVal Chk As Boolean = False, _
Optional d As Integer) As String
    Dim wArr    '定义数组
   
     '╱*若[wStr]或者[Mark]为空,则退出过程*╱
    If (wStr = "") Or (Mark = "") Then Exit Function
   
     '╱*若[Mark]的长度大于[wStr],则退出过程*╱
    If Len(Mark) > Len(wStr) Then Exit Function
   
    wArr = Split(wStr, Mark)    '取出各段段值并保存到数组中
    d = UBound(wArr) + 1        '通过返回数组的上界得知总段数
   
    '╱*未找到分隔符时*╱
    If d = 1 Then
        d = 0
        Exit Function
    Else
        '╱*找到分隔符时且[n]为零或负*╱
        If n < 1 Then Exit Function
    End If
   
    '╱*若[Chk]值为 "True",则启用“倒数第[n]”*╱
    If Chk = True Then n = d - n + 1
    Get_nStr = wArr(n - 1) '取出第[n]段字符串
End Function

'-------------------------------------------------{Set_nStr『现代版』}-------------------------------------------------
'【函数说明】:
'设定以非空字符串中的连续单个或多个子字符串为分隔符隔开的第[n]段字符串,简称“设段”函数。
'【参数含义】:
'① [wStr]   → 非空字符串;
'② [Mark]   → 分隔符;
'③ [n]      → 被分隔符隔开的第[n]段字符串;
'④ [NewStr] → 欲设定的新字符串;
'④ [Chk]    → 可选参数:是否启用“倒数第[n]”(缺省值为False,即“正数第[n]”);
'⑤ [d]      → 可选参数:被分隔符分隔的总段数,使用前必须定义相关变量(防止ByRef参数类型不符)。
'【参数范围】:
'[n] → "1 - 32767" 之间的整数(负数和零没有实际意义)。
'[d] → "0 - 32767" 之间的整数(负数没有实际意义)。
'---------------------------------------------------------------------------------------------------------------------------------
Function Set_nStr( _
ByVal wStr As String, _
ByVal Mark As String, _
ByVal n As Integer, _
ByVal NewStr As String, _
Optional ByVal Chk As Boolean = False, _
Optional d As Integer) As String
    Dim wArr    '定义数组
   
     '╱*若[wStr]或者[Mark]为空,则设定段值后退出过程*╱
    If (wStr = "") Or (Mark = "") Then Set_nStr = wStr: Exit Function

     '╱*若[Mark]的长度大于[wStr],则先强制设定第[n]段字符串为[wStr],然后退出过程*╱
    If Len(Mark) > Len(wStr) Then Set_nStr = wStr: Exit Function
   
    wArr = Split(wStr, Mark)    '取出各段段值并保存到数组中
    d = UBound(wArr) + 1        '通过返回数组的上界得知总段数
   
    '╱*未找到分隔符时*╱
    If d = 1 Then
        d = 0
        Set_nStr = wStr
        Exit Function
    Else
        '╱*找到分隔符时且[n]为零或负*╱
        If n < 1 Then
            Set_nStr = wStr
            Exit Function
        End If
    End If
   
    '╱*若[Chk]值为 "True",则启用“倒数第[n]”*╱
    If Chk = True Then n = d - n + 1
    wArr(n - 1) = NewStr        '用欲设定的新字符串[NewStr]替换第[n]段字符串
    Set_nStr = Join(wArr, Mark) '通过连接数组[wArr]中的多个子字符串来设定第[n]段字符串
End Function

经典版与现代版的区别是:
①.经典版代码较多,复杂不易接受,采用了“智能段位定循环”的核心算法:先将字符串的段数除以2(余数四舍五入)然后根据段值所在的位置进行智能判断。如果位于前半段,就从首字符串开始循环;如果位于后半段,就从尾字符串开始循环。这样不仅减少了循环次数,而且还提高了运算速度,可谓是“一举两得”!
②.现代版代码较少,简洁易于接受,其核心算法只是利用了封装在VBA(Visual Basic for Applications)库里面的现成的“Split”和“Join”函数,且无需编写大量代码便可轻松实现!

个人观点:经典版代码虽多,但是由于其引入了“智能段位定循环”的核心算法,因此其运算速度应该可以跟现代版的相抗衡,甚至可以说是更快。在实际应用中推荐使用现代版(毕竟代码行少多了),不过经典版中的算法可以说是揭示了现代版中的“Split”和“Join”这两个函数的本质,有兴趣的读者可以研究一下,用用也无妨,呵呵~

调用方法:
对于同一个段值处理函数,不管是哪个版本,其调用方法都是一样的。
Dim d%,s$,MyStr$
s = "文本框◆Travel◆Korea◆67……椰树虾"
“取段”函数的调用:
'取出正数第2段段值:Travel(两种方法)
1.MyStr=Get_nStr(s, "◆", 2,)
2.MyStr=Get_nStr(s, "◆", 2, False)
'取出正数第2段段值:Travel,并返回总段数:4(两种方法)
1.MyStr=Get_nStr(s, "◆", 2, , d)
2.MyStr=Get_nStr(s, "◆", 2, False, d)
'取出倒数第2段段值:Korea
MyStr=Get_nStr(s, "◆", 2, True)
'取出倒数第2段段值:Korea,并返回总段数:4
MyStr=Get_nStr(s, "◆", 2, True, d)

“设段”函数的调用:
'设定正数第2段段值:文本框◆完美时空◆Korea◆67……椰树虾(两种方法)
1.MyStr = Set_nStr(s, "◆", 2, "完美时空")
2.MyStr = Set_nStr(s, "◆", 2, "完美时空", False)
'设定正数第2段段值:文本框◆完美时空◆Korea◆67……椰树虾,并返回总段数:4(两种方法)
1.MyStr = Set_nStr(s, "◆", 2, "完美时空", , d)
2.MyStr = Set_nStr(s, "◆", 2, "完美时空", False, d)
'设定倒数第2段段值:文本框◆完美时空◆Korea◆67……椰树虾
MyStr = Set_nStr(s, "◆", 2, "完美时空", True)
'设定倒数第2段段值:文本框◆完美时空◆Korea◆67……椰树虾,并返回总段数:4
MyStr = Set_nStr(s, "◆", 2, "完美时空", True, d)


>>实例说明
设某配置文件(D:\Config.ini)中有如下一段内容:
[Default]
Languages=|English|French|Chinese|Japanese|Russian|
……
以“|”为分隔符分析Languages等号右边的字符串:
1.根据“字符串的分段定律”,该字符串被分隔符“|”分隔的段数为7;
2.各段段值分别为:[空]、English、French、Chinese、Japanese、Russian、[空];

下面以处理Languages的第五段段值为例来介绍Get_nStr、Set_nStr在配置文件方面的应用:

'新建VB工程,添加两个按钮(Command1和Command2)和一个标签(Label1),实现代码如下:
'声明用于操作配置文件的API函数及相关变量和常量
Option Explicit
Private Declare Function GetPrivateProfileString Lib "kernel32" _
    Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, ByVal lpDefault As String, _
    ByVal lpReturnedString As String, ByVal nSize As Long, _
    ByVal lpFileName As String) As Long
Private Declare Function WritePrivateProfileString Lib "kernel32" _
    Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, ByVal lpString As Any, _
    ByVal lpFileName As String) As Long
Private Buff As String * 255 '定义缓冲区大小
Private StrOut, StrIn As Long '分别定义:复制到缓冲区的字节数量、写入的字符串值
Private Const FileName = "D:\Config.ini" '设置文件路径

'取出第五段段值
Private Sub Command1_Click()
    Dim GetBuff5 As String
    On Error Resume Next '防止出错
    '╱*读出"Languages"*╱
    StrOut = GetPrivateProfileString("Default", "Languages", vbNullString, Buff, 255, FileName)
    '╱*取出[Buff]中第五段字符串*╱
    GetBuff5 = Get_nStr(Buff, "|", 5)
    MsgBox "第五段字符串为:" & GetBuff5, vbOKOnly, "取出段值"
End Sub

'设定第五段段值
Private Sub Command2_Click()
    Dim SetBuff5 As String
    On Error Resume Next '防止出错
    '╱*读出"Languages"*╱
    StrOut = GetPrivateProfileString("Default", "Languages", vbNullString, Buff, 255, FileName)
    '╱*设定[Buff]中第五段字符串为"English"*╱
    SetBuff5 = Set_nStr(Buff, "|", 5, "English")
    '╱*检测是否设定:若未设定,则将[SetBuff5]写入Config.ini;若已设定,则给出提示。*╱
    If SetBuff5 <> Buff Then
        StrIn = WritePrivateProfileString("Default", "Languages", SetBuff5, FileName)
        MsgBox "设定成功,您现在可以打开“D:\Config.ini”查看", vbOKOnly, "设定段值"
    Else
        MsgBox "您已设定过,无需重复设定!", vbOKOnly, "设定段值"
    End If
End Sub

这两个版本的函数能有效处理各种字符串,通用性极强,如能举一反三,应用到其他程序中,定会为您的程序增辉。

另外说明一下:任何一个事物都不可能十全十美(这两个版本的函数也不例外),但是可以做到尽善尽美(本人也尽力了)。为防止数据发生溢出错误,最好在调用前加一句“On Error Resume Next”,如果不加对于绝大多数字符串的处理也是没有问题的。总之:千万不怕怕万一,自己决定吧!