Это правильный метод получения целых чисел из строки в VBA - PullRequest
1 голос
/ 06 мая 2020

Я скопировал текстовую информацию из старых файлов, и мне нужно получить четкие числовые данные, помещенные в строки. У меня есть следующие строки:
"сезон: 1983 колония: 23 вес колонии: 4 кг, выход: 12 кг"
"сезон: 1983 колония: - вес колонии: 5 кг, выход: 14 кг"

Итак, я создал функцию, которая принимает строку сырых данных и возвращает массив целых чисел.

Function getClearBeeData(rawData As Variant) As Integer()
  Dim retValue(4) As Integer 'array where each found number stored
  Dim strTempString As String 'temporary string to hold current number
  Dim i, k As Integer 'i counter for original string, k counter for array position
  Dim token As Boolean 'token shows whether previous chars were number
  token = False

  For i = 1 To Len(rawData)   'go through original string

    If IsNumeric(Mid(rawData, i, 1)) Then   'if current char is numeric
      strTempString = strTempString & Mid(rawData, i, 1)  'add current char to remporary string
      token = True  'show that current position is within numbers
    ElseIf Mid(rawData, i, 1) = Chr(45) Then  'in string a symbol "-" can appear
      strTempString = "0"
      token = True
    ElseIf Not IsNumeric(Mid(rawData, i, 1)) And token = True Then  'if current char is not numeric and token shows that previous char was number
      retValue(k) = CInt(strTempString)   'convert temporary string to int and write in to the array
      k = k + 1   'go to next array position
      token = False   'switch token to show that current position is not within numbers
      strTempString = ""  'delete stored data from temporary string
    End If

  Next

  If Len(strTempString) > 0 Then
    retValue(k) = CInt(strTempString) 'if original string ended with numbers, write that numbers to array
  End If
  getClearBeeData = retValue
End Function

И тестовую подпрограмму для печати данных.

Sub printClearBeeData()
  Dim rawData As String
  Dim clearDataArr() As Integer
  Dim i As Integer
  rawData = "season: 1983 colony: 12 colony weight: - kg yeild: 16 kg"
  clearDataArr = getClearBeeData(rawData)
  For i = LBound(clearDataArr) To UBound(clearDataArr) - 1
    Debug.Print clearDataArr(i)
  Next
End Sub

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

Ответы [ 4 ]

4 голосов
/ 06 мая 2020

Поскольку ваш строковый шаблон всегда будет одним и тем же (где одна или несколько подстрок могут быть представлены знаком «-»), RegEx может стать простым и легким в реализации инструментом для извлечения этих подстрок. Очень простой пример c, который поможет вам начать:

Sub Test()

Dim rawData As String, ClearBeeData as Object

rawData = "season: 1983 colony: 12 colony weight: - kg yeild: 16 kg"
With CreateObject("vbscript.regexp")
    .Global = True
    .Pattern = "\d+|-"
    Set ClearBeeData = .Execute(rawData)
End With

For Each el In ClearBeeData
    Debug.Print Val(el)
Next

End Sub

Ссылка, предоставленная @Peh, предоставит вам массу информации, но здесь можно сделать несколько небольших замечаний:

  • .Global = True - Если это было установлено на False, мы также можем получить первое совпадение. Если установлено значение True, мы можем получить все совпадения.
  • .Pattern = "\d+|-" - Очень простой шаблон c для поиска внутри полной строки, где \d просто сокращение от класса символов [0-9] и + ищет в подстроке хотя бы один символ (или более длинный). Символ вертикальной черты представляет OR, поэтому, если позиция не содержит чисел, мы можем искать - в качестве альтернативы.
  • .Execute(...) - вернет объект типа Matchcollection найденных совпадений . Обратите внимание, что при обнаружении совпадений может возникнуть ошибка. Здесь нет ничего страшного, когда строки имеют один и тот же шаблон, но для справки в будущем вы можете захотеть включить .Test, прежде чем мы попытаемся .Execute.
  • Val(el) - поскольку все элементы в возвращенная коллекция является текстом, мы можем использовать Val, чтобы вернуть их как числа, если хотите. Замечательная особенность заключается в том, что Val("-") вернет 0. Итак, в приведенном выше примере ваши результаты будут:

    1983
    12
    0
    16
    
3 голосов
/ 07 мая 2020

Split в сочетании с Filter позволяет использовать только один l oop

Просто для удовольствия и в дополнение к правильному ответу @FaneDuru, я демонстрирую, как использовать Функция Split() объединена с Filter(), что позволяет избежать дополнительной передачи элементов массива в функции справки вторым l oop.

Sub TrickySplit()
    Dim rawData As String
    rawData = "season: 1983 colony: 12 colony weight: - kg yeild: 16 kg"

    '[1] split to tokens omitting the 1st element "seasons"
    '    ~> 1983 colony, 12 colony weight, - kg yeild, 16 kg
    Dim words: words = Split(rawData, ": "): words = Filter(words, words(0), False)

    '[2] convert number strings or "-" to values
    Dim i: For i = 0 To UBound(words): words(i) = Val(words(i)): Next

    '[3] optional (display results in VB Editors Immediate Window
    '    ~> 1983,12,0,16
    Debug.Print Join(words, ",")
End Sub

Дополнительные подсказки

ad) [1]: классический Split() через разделитель : " приводит к 0- на основе массива:

   season|1983 colony|12 colony weight|- kg yeild|16 kg 

Первый элемент words(0) ~> season не требуется и может быть немедленно удален функцией Filter(), что приводит к:

   1983 colony, 12 colony weight, - kg yeild, 16 kg

ad) [2]: функция Val() words(i) = Val(words(i)) использует тот факт, что она игнорирует следующие символы, даже интерпретируя "-" как 0

(поэтому нет необходимости выполнять теоретическое второе разбиение через words(i) =Val(Split(words(i), " ")(0)), чтобы удалить строку приложения) .

3 голосов
/ 06 мая 2020

Протестируйте также этот (стандартный VBA) фрагмент кода, пожалуйста. Это немного короче и проще, чем ваше:

Private Function extractIntegers(rowData As String, strDelim As String) As Variant
 Dim arr As Variant, arrInt(3) As Variant, i As Long

 arr = Split(rowData, strDelim)

 For i = 1 To UBound(arr)
    arrInt(i - 1) = val(arr(i))
 Next i
 extractIntegers = arrInt
End Function

Процедура для его проверки будет выглядеть так:

Sub testexractIntegers()
  Dim rowData As String, El As Variant, arrInt As Variant
  rowData = "season: 1983 colony: 23 colony weight: 4 kg yeild: 12 kg"
  'rowData = "season: 1983 colony: - colony weight: 4 kg yeild: 12 kg"
  arrInt = extractIntegers(rowData, ": ")
  For Each El In arrInt
    Debug.Print Int(El)
  Next
End Sub

Если вы раскомментируете второе определение rowData (содержащее "- "), массив вернет 0 для указанного c элемента массива

1 голос
/ 06 мая 2020

Решение, которое не полагается на регулярное выражение, но требует, чтобы числа всегда были окружены пробелами.

Функция возвращает словарь сценариев Index vs Number, где Index - это первый символ числа в строка необработанных данных, а Number - это строка numeri c, преобразованная в тип Double.

Option Explicit

Sub TestGetClearBeeData()

    Dim mySD As Scripting.Dictionary

    Set mySD = getClearBeeData("season: 1983 colony: - colony weight: 5 kg yeild: 14 kg")

    Dim myItem As Variant
    For Each myItem In mySD

        Debug.Print myItem, mySD.Item(myItem)

    Next

End Sub


Function getClearBeeData(ByVal ipRawData As String) As Scripting.Dictionary

    Dim myItems As Variant
    myItems = Split(Replace(ipRawData, "-", "0"))

    Dim myItem As Variant
    Dim myNumbers As Scripting.Dictionary: Set myNumbers = New Scripting.Dictionary
    Dim myLen As Long: myLen = 1
    For Each myItem In myItems

        If IsNumeric(myItem) Then

            myNumbers.Add myLen, CDbl(myItem)

        End If

        myLen = myLen + Len(myItem) + 1

    Next

    Set getClearBeeData = myNumbers

End Function
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...