Пытаюсь извлечь данные из фигурных скобок, но не работает

Мне нужно синхронизировать значения в фигурных скобках {} в столбце C и сопоставить их с идентификатором пользователя в столбце F, как показано ниже.

Например. на листе электронной почты

введите здесь описание изображения

становится этим на новом листе

введите здесь описание изображения

Sub CopyConditional()
Dim wshS As Worksheet
Dim WhichName As String

Set wshS = ActiveWorkbook.Sheets("Emails")
WhichName = "NewSheet"

Const NameCol = "C"
Const FirstRow = 1

Dim LastRow As Long
Dim SrcRow As Long
Dim TrgRow As Long
Dim wshT As Worksheet
Dim cpt As String
Dim user As String
Dim computers() As String
Dim computer As String

On Error Resume Next
Set wshT = Worksheets(WhichName)
If wshT Is Nothing Then
    Set wshT = Worksheets.Add(After:=wshS)
    wshT.Name = WhichName
End If
On Error GoTo 0

If wshT.Cells(1, NameCol).value = "" Then
    TrgRow = 1
Else
    TrgRow = wshT.Cells(wshT.Rows.Count, NameCol).End(xlUp).Row + 1
End If

LastRow = wshS.Cells(wshS.Rows.Count, NameCol).End(xlUp).Row
For SrcRow = FirstRow To LastRow
    cpt = wshS.Range("C" & SrcRow).value
    user = wshS.Range("F" & SrcRow).value

    If InStr(cpt, ":") Then
        cpt = Mid(cpt, InStr(1, cpt, ":") + 1, Len(cpt))
    End If

    If InStr(cpt, ";") Then
        computers = Split(cpt, ";")
        For i = 0 To UBound(computers)
            If computers(i) <> "" Then
                wshT.Range("A" & TrgRow).value = user
                wshT.Range("B" & TrgRow).value = Mid(Left(computers(i), Len(computers(i)) - 1), 2)
                TrgRow = TrgRow + 1
            End If
        Next
    Else
        computer = cpt
        If computer <> "" Then
            wshT.Range("A" & TrgRow).value = user
            wshT.Range("B" & TrgRow).value = Mid(Left(computer, Len(computer) - 1), 2)
            TrgRow = TrgRow + 1
        End If
    End If

Next SrcRow

End Sub

Мне удалось решить эту проблему с помощью приведенного выше кода, но есть 3 мелкие проблемы:

1) Первая фигурная скобка всегда копируется, как мне ее опустить, чтобы что-то вроде {Computer1 выглядело как Computer 1

введите здесь описание изображения

2) Там где два компа подряд, то вывод выглядит примерно так:

введите здесь описание изображения

когда он действительно должен быть разделен на две разные строки, т.е.

User 1 | Computer 1
User 1 | Computer 2

3) Если после последней фигурной скобки есть текст с текстом, например. {Computer1};{Computer2};Request submitted, то этот текст добавляется как новая строка, я не хочу этого, я хочу, чтобы он был опущен, например.

введите здесь описание изображения

должно быть просто:

User 1 | Computer 1
User 1 | Computer 2

Как мне исправить эти проблемы?


person methuselah    schedule 05.06.2014    source источник


Ответы (3)


Попробуй это:

Sub Collapse()
    Dim uRng As Range, cel As Range
    Dim comps As Variant, comp As Variant, r As Variant, v As Variant
    Dim d As Dictionary '~~> Early bind, for Late bind use commented line
    'Dim d As Object
    Dim a As String

    With Sheet1 '~~> Sheet that contains your data
        Set uRng = .Range("F1", .Range("F" & .Rows.Count).End(xlUp))
    End With

    Set d = CreateObject("Scripting.Dictionary")
    With d
        For Each cel In uRng
            a = Replace(cel.Offset(0, -3), "{", "}")
            comps = Split(a, "}")
            Debug.Print UBound(comps)
            For Each comp In comps
                If InStr(comp, "Computer") <> 0 _
                And Len(Trim(comp)) <= 10 Then '~~> I assumed max Comp# is 99
                    If Not .Exists(cel) Then
                        .Add cel, comp
                    Else
                        If IsArray(.Item(cel)) Then
                            r = .Item(cel)
                            ReDim Preserve r(UBound(r) + 1)
                            r(UBound(r)) = comp
                            .Item(cel) = r
                        Else
                            r = Array(.Item(cel), comp)
                            .Item(cel) = r
                        End If
                    End If
                End If
            Next
        Next
    End With

    For Each v In d.Keys
        With Sheet2 '~~> sheet you want to write your data to
            If IsArray(d.Item(v)) Then
                .Range("A" & .Rows.Count).End(xlUp).Offset(1, 0) _
                    .Resize(UBound(d.Item(v)) + 1) = v
                .Range("B" & .Rows.Count).End(xlUp).Offset(1, 0) _
                    .Resize(UBound(d.Item(v)) + 1) = Application.Transpose(d.Item(v))
            Else
                .Range("A" & .Rows.Count).End(xlUp).Offset(1, 0) = v
                .Range("B" & .Rows.Count).End(xlUp).Offset(1, 0) = d.Item(v)
            End If
        End With
    Next
    Set d = Nothing

End Sub

В приведенном выше коде используется Функция замены и разделения для передачи строки в массив.

a = Replace(cel.Offset(0, -3), "{", "}") '~~> standardize delimiter
comps = Split(a, "}") '~~> split using standard delimiter

Затем информация передается в объект словаря с использованием пользователя в качестве ключа и компьютеров в качестве элементов.
Мы фильтруем элементы, передаваемые в словарь, с помощью функций Instr и Len.

If InStr(comp, "Computer") <> 0 _
And Len(Trim(comp)) <= 10 Then

Как я уже говорил, я предположил, что ваш максимальный номер компьютера равен 99.
В противном случае измените 10 на любую длину, которую вам нужно проверить.
Наконец, мы возвращаем словарную информацию на целевой рабочий лист.
Примечание. : Вам нужно добавить ссылку на Microsoft Scripting Runtime, если вы предпочитаете раннее связывание

Результат: я попробовал это на небольшой выборке данных, основанной на том, как я вижу это у вас, SS.

Итак, если у вас есть эти данные на Листе 1:
введите здесь описание изображения

Будет выводить данные в Sheet2 следующим образом:
введите здесь описание изображения

person L42    schedule 06.06.2014
comment
Я получаю сообщение об ошибке user-defined type not defined в Dim d As Dictionary - person methuselah; 06.06.2014
comment
Смотрите мою заметку. Или вы можете использовать закомментированную строку вместо Dim d As Object. - person L42; 06.06.2014
comment
Теперь он работает, но на листе2 нет вывода — см. код на pastebin.com/Bn5CYE6b. - person methuselah; 08.06.2014
comment
@methuselah Sheet1 и Sheet2 — это кодовые имена рабочих листов. Убедитесь, что вы проверяете правильные листы. - person L42; 09.06.2014
comment
@methuselah кстати, вот мой тестовый файл, который я использовал во время тестирования wikisend.com/download/397838 - person L42; 09.06.2014
comment
Я просто дважды щелкнул одну из ячеек, и вот как выглядят данные imgur.com/e8hdd7O, я думаю вот почему код не работал - я добавил его здесь ge.tt/7GfAGlj1/ v/0?c - person methuselah; 10.06.2014

Я использую пользовательскую функцию синтаксического анализа для этого типа операций:

Sub CopyConditional()
  ' some detail left out
  Dim iRow&, Usern$, Computer$, Computers$
  For iRow = ' firstrow To lastrow
    Usern = Sheets("Emails").Cells(iRow, "F")
    Computers = Sheets("Emails").Cells(iRow, "C")
    Do
      Computer = zParse(Computers) ' gets one computer
      If Computer = "" Then Exit Do
      ' Store Computer and Usern
    Loop
  Next iRow
End Sub

Function zParse$(Haystack$) ' find all {..}
  Static iPosL& '
  Dim iPosR&
  If iPosL = 0 Then iPosL = 1
  iPosL = InStr(iPosL, Haystack, "{") ' Left
  If iPosL = 0 Then Exit Function ' no more
  iPosR = InStr(iPosL, Haystack, "}") ' Right
  If iPosR = 0 Then MsgBox "No matching }": Stop
  zParse = Mid$(Haystack, iPosL + 1, iPosR - iPosL - 1)
  iPosL = iPosR
End Function
person dcromley    schedule 06.06.2014

1) Используйте функцию Mid, чтобы удалить первый символ:

str = "{Computer1"
str = Mid(str,2)

теперь ул = "Компьютер1"

2) Вы можете использовать функцию Split, чтобы разделить их и объединить с функцией Mid выше.

str = "{Computer1}{Computer2}"
splt = Split(str,"}")
for a = 0 to Ubound(splt)
     result = Mid(splt(a),2)
next a

3) Добавьте условный оператор в вышеуказанный цикл

str = "{Computer1}{Computer2}"
splt = Split(str,"}")
for a = 0 to Ubound(splt)
     if Left(splt(a),1) = "{" then result = Mid(splt(a),2)
next a

Используйте этот цикл и отправьте каждый результат в нужную ячейку (в цикле for-next), и все будет хорошо.

person MatthewHagemann    schedule 05.06.2014
comment
Спасибо, Мэтью. Как мне включить это в свой код? - person methuselah; 06.06.2014
comment
Запустите его прямо перед тем, как поместить строку в ячейку, заменив str строковой переменной, которую вы используете (я просто использовал ее для демонстрации). Я не на своем компьютере, иначе я бы включил его для вас. - person MatthewHagemann; 06.06.2014
comment
@methuselah - вы хотите splt(a,.. вместо splt(0,.. ? - person dcromley; 06.06.2014