解析和比较复杂的字符串

时间:2011-04-13 23:22:04

标签: string excel vba

我希望有人可以帮我解决VBA Excel宏问题 我收到了Excel 2007中的工作表,其中包含一列中的产品名称,我需要将其排序为逻辑格式,以便我可以使用它。但是,列表本身没有任何逻辑顺序,长达10 000行,我每个月都要这样做!!

基本上,我想要做的是搜索大多数条目共有的某些关键字,并将它们移动到不同列中的单独单元格中(但与原始条目位于同一行中)。

关于关键字:有3种不同的类型,其中两种我有完整的清单。

关键字示例:有些是诸如厘米(厘米),毫米(毫米),米(米)等的度量。然后还有其他关键字,如%,最后是最后一组关键字,木材,塑料,玻璃等。

如果这不够复杂,那么测量(例如cm)在某些情况下是重复的并且是重要的细节,所以我不能将它们分开,但理想情况下它们会在两个相邻的单元格中相似。

幸运的是,每个度量后面都有一个空格,%符号和项目材料。

从右到左工作是我能想到实现这一点的最简单方法,因为字符串中的第一个描述在条目之间变化很大,并且可以保持原样。

因此,下面是一个示例字符串,假设这是在单元格A1中。 (倒置的逗号不包含在字符串中,单词“by”只出现在大约100个案例中。通常它会丢失...)

“椅腿木100%1m乘20cm”

我最好将字符串拆分为单元格,如下所示

Cell B1 - Chair Leg  
Cell C1 - Wood  
Cell D1 - 1m  
Cell E1 - 2cm  
Cell F1 - 100%  

将%度量放在同一列中会非常有帮助

任何人都可以请帮助我这个或宏的开头这样做,然后向下移动列表 - 我尝试使用一些基本的“查找”和“len”公式,但我真的在我的智慧结束如何处理这个!

3 个答案:

答案 0 :(得分:1)

任务归结为定义输入数据结构的健壮定义。

提供候选定义可能

的信息
<Description, one or more words> <Material, one word> <A value followd by %> <Dimension A> <optional "by">  <Dimension B>

以下宏将处理符合此规范的数据。定义可能需要 扩展,例如双字材料(例如低碳钢)

如果任何行不符合,您将需要添加错误处理,例如字符串中没有%,或者字符串中其他位置的%字符

Option Explicit

Dim dat As Variant

Sub ProcessData()
    Dim r As Range
    Dim i As Long

    Set r = Intersect(ActiveSheet.UsedRange, ActiveSheet.Columns(1)).Resize(, 5)
    dat = r
    For i = 1 To UBound(dat, 1)
        ParseRow i, CStr(dat(i, 1))
    Next
    r = dat
    ActiveSheet.Columns(5).Style = "Percent"

End Sub


Sub ParseRow(rw As Long, s As String)
    'Chair Leg Wood 100% 1m by 20cm

    Dim i As Long
    Dim sDim As String, sPCnt As String, sMat As String, sDesc As String
    Dim sA As String, sB As String

    i = InStr(s, "% ")
    sDim = Trim(Replace(Mid(s, i + 2), " by ", " "))  ' text to right of %, remove "by"
    sA = Trim(Left(sDim, InStr(sDim, " ")))           ' split dimension string in two
    sB = Trim(Mid(sDim, InStr(sDim, " ")))
    s = Left(s, i)

    i = InStrRev(s, " ")
    sPCnt = Mid(s, i + 1)        ' text back to first space before %
    s = Trim(Left(s, i))  

    i = InStrRev(s, " ")         ' last word in string
    sMat = Mid(s, i + 1)
    sDesc = Trim(Left(s, i))     ' whats left


    dat(rw, 1) = sDesc
    dat(rw, 2) = sMat
    dat(rw, 3) = sA
    dat(rw, 4) = sB
    dat(rw, 5) = sPCnt

End Sub

答案 1 :(得分:0)

首先,我使用Split函数将部分分成一个数组,这将避免大多数字符串函数和字符串数学:

Dim parts As Variant
parts = Split(A1)

然后,我会对每个部分进行比较 最后,我将连接我没有突破的部分,并将所有部分放在工作表上。

这是基于你的例子,每个部分之间都有空格,虽然类似的东西可能会起作用,你只需要对每个部分做更多的工作。

答案 2 :(得分:0)

这是我对它的刺痛。我们可以使用大约10个例子,但这应该是一个开始。要使用,请选择包含描述的一列范围并运行SplitProduct。它将它分成每个单元格的右侧。

Sub SplitProducts()

    Dim rCell As Range
    Dim vaSplit As Variant
    Dim i As Long
    Dim aOutput() As Variant
    Dim lCnt As Long

    Const lCOLDESC As Long = 1
    Const lCOLMAT As Long = 2
    Const lCOLPCT As Long = 3
    Const lCOLREM As Long = 4

    If TypeName(Selection) = "Range" Then
        If Selection.Columns.Count = 1 Then
            For Each rCell In Selection.Cells
                'split into words
                vaSplit = Split(rCell.Value, Space(1))
                ReDim aOutput(1 To 1, 1 To 1)

                'loop through the words
                For i = LBound(vaSplit) To UBound(vaSplit)
                    Select Case True
                        Case IsPercent(vaSplit(i))
                            'percents always go in the same column
                            lCnt = lCOLPCT
                            If UBound(aOutput, 2) < lCnt Then
                                ReDim Preserve aOutput(1 To 1, 1 To lCnt)
                            End If
                            aOutput(1, lCnt) = vaSplit(i)
                        Case IsInList(vaSplit(i))
                            'list items always go in the same column
                            lCnt = lCOLMAT
                            ReDim Preserve aOutput(1 To 1, 1 To lCnt)
                            If UBound(aOutput, 2) < lCnt Then
                                ReDim Preserve aOutput(1 To 1, 1 To lCnt)
                            End If
                            aOutput(1, lCnt) = vaSplit(i)
                        Case IsMeasure(vaSplit(i))
                            'measurements go in the last column(s)
                            If UBound(aOutput, 2) < lCOLREM Then
                                lCnt = lCOLREM
                            Else
                                lCnt = UBound(aOutput, 2) + 1
                            End If
                            ReDim Preserve aOutput(1 To 1, 1 To lCnt)
                            aOutput(1, lCnt) = vaSplit(i)
                        Case Else
                            'everything else gets concatentated in the desc column
                            aOutput(1, lCOLDESC) = aOutput(1, lCOLDESC) & " " & vaSplit(i)
                    End Select
                Next i

                'remove any extraneous spaces
                aOutput(1, lCOLDESC) = Trim(aOutput(1, lCOLDESC))

                'write the values to the left of the input range
                rCell.Offset(0, 1).Resize(1, UBound(aOutput, 2)).Value = aOutput

            Next rCell
        Else
            MsgBox "Select a one column range"
        End If
    End If

End Sub

Function IsPercent(ByVal sInput As String) As Boolean

    IsPercent = Right$(sInput, 1) = "%"

End Function

Function IsInList(ByVal sInput As String) As Boolean

    Dim vaList As Variant
    Dim vaTest As Variant

    'add list items as needed
    vaList = Array("Wood", "Glass", "Plastic")
    vaTest = Filter(vaList, sInput)

    IsInList = UBound(vaTest) > -1

End Function

Function IsMeasure(ByVal sInput As String) As Boolean

    Dim vaMeas As Variant
    Dim i As Long

    'add measurements as needed
    vaMeas = Array("mm", "cm", "m")

    For i = LBound(vaMeas) To UBound(vaMeas)
        'any number of characters that end in a number and a measurement
        If sInput Like "*#" & vaMeas(i) Then
            IsMeasure = True
            Exit For
        End If
    Next i

End Function

无法保证10k行的速度很快。