Конечные конечные результаты:
Мне было интересно, изменились ли результаты ниже, если строка была длиннее. Я выполнял точно такие же тесты на одном компьютере, за исключением того, что каждая ячейка имела случайную строку из 34 символов, а не четыре. Это были результаты:
Comintern (Regexp): 136.1 ms
brettdj (Regexp): 139.9 ms
Slai (Regexp): 158.4 ms
*Original Regex: 161.0 ms*
Comintern (AN): 170.1 ms
Comintern (Hash): 183.6 ms
ThunderFrame: 232.9 ms
*Original replace: 372.9 ms*
*Original InStr: 478.1 ms*
CallumDA33: 1218.1 ms
Это действительно показывает скорость Regex - все решения, использующие Regex.replace, значительно быстрее, причем лучше всего реализовать Comintern.
Итак, если строки длинны, используйте массивы, если они короткие, используйте буфер обмена. Если вы не уверены, оптимальным результатом будет использование массивов, но это может пожертвовать небольшой производительностью на коротких строках.
Конечные результаты:
Большое спасибо за все ваши предложения, ясно, что мне еще многое предстоит узнать. Я думал об этом вчера, поэтому решил перепробовать все дома. Вот окончательные результаты, основанные на применении каждого из них до 30 000 четырехзначных строк.
Мой компьютер дома - это Intel i7 @3.6 ГГц, 8 ГБ оперативной памяти, 64-разрядные версии Windows 10 и Excel 2016. Аналогичные условия для этого в том, что у меня есть процессы, работающие в фоновом режиме, но я не делаю ничего тесты.
Original replace: 97.67 ms
Original InStr: 106.54 ms
Original Regex: 113.46 ms
ThunderFrame: 82.21 ms
Comintern (AN): 96.98 ms
Comintern (OR): 81.87 ms
Comintern (Hash): 101.18 ms
brettdj: 81.66 ms
CallumDA33: 201.64 ms
Slai: 68.38 ms
Поэтому я принял ответ Slai, поскольку он, безусловно, является самым быстрым для общей реализации, но я перезапущу их всех на работу против фактических данных, чтобы проверить, что это все еще работает.
Оригинальное сообщение:
У меня есть массив в Excel, который является списком номеров деталей. Мне нужно, чтобы каждый элемент массива был буквенно-цифровым, например
ABC123-001 -> ABC123001
ABC123/001 -> ABC123001
ABC123001 -> ABC123001
Каков самый быстрый способ сделать это?
Для контекста наши номера деталей могут иметь разные формы, поэтому я пишу функцию, которая находит наилучшее соответствие в заданном диапазоне. На данный момент часть функции, которая делает все буквенно-цифровое, занимает около 50 мс, тогда как остальная часть функции занимает около 30 мс. Я также не могу избежать использования Excel.
Я сам проделал определенную работу (см. ответ ниже), но главная проблема заключается в том, что я должен прокручивать каждый элемент массива один за другим - может ли быть лучший способ? Я также никогда не запускал тесты раньше, поэтому любые отзывы об их улучшении будут очень оценены.
Вот что я пробовал до сих пор.
Я использую MicroTimer, а мой компьютер имеет Intel i5 @2.5GHz, 4 ГБ оперативной памяти, 64-битную Windows 7. я У меня есть процессы, работающие в фоновом режиме, но я не активно делаю что-либо еще, пока они запускаются.
Я создал 30 000 строк случайных символов, используя этот код:
=CHAR(RANDBETWEEN(1,60))&CHAR(RANDBETWEEN(48,57))&CHAR(RANDBETWEEN(37,140))&CHAR(RANDBETWEEN(37,140))
(обратите внимание, как мы останавливаем первый символ в 60, потому что '=' - char(61)
, и мы хотим, чтобы Excel не интерпретировал это как формулу. Также мы вынуждаем второго символа быть числом, чтобы мы могли гарантировать хотя бы один буквенно-цифровой символ.)
1. Использование цикла на основе случаев. Среднее время: 175 мс
Используя функцию в этом сообщении, мы загружаем диапазон в массив, применяем функцию к каждому элементу массива и вставляем его обратно. Код:
Function AlphaNumericOnly(strSource As Variant) As String
Dim i As Integer
Dim strResult As String
For i = 1 To Len(strSource)
Select Case Asc(Mid(strSource, i, 1))
Case 48 To 57, 65 To 90, 97 To 122: 'include 32 if you want to include space
strResult = strResult & Mid(strSource, i, 1)
End Select
Next
AlphaNumericOnly = strResult
End Function
Sub Replace()
Dim inputSh As Worksheet
Dim inputRng As Range
Set inputSh = Sheets("Data")
Set inputRng = inputSh.Range("A1:A30000")
Dim outputSh As Worksheet
Dim outputRng As Range
Set outputSh = Sheets("Replace")
Set outputRng = outputSh.Range("A1:A30000")
Dim time1 As Double, time2 As Double
time1 = MicroTimer
Dim arr As Variant
arr = inputRng
Dim i As Integer
For i = LBound(arr) To UBound(arr)
arr(i, 1) = AlphaNumericOnly(arr(i, 1))
Next i
outputRng = arr
time2 = MicroTimer
Debug.Print (time2 - time1) * 1000
End Sub
2. Использование InStr() для проверки каждого символа. Среднее время: 201 мс
Определите строку допустимых значений. Проверяйте один за другим, если в элементах массива отображаются допустимые значения:
Sub InStr()
Dim inputSh As Worksheet
Dim inputRng As Range
Set inputSh = Sheets("Data")
Set inputRng = inputSh.Range("A1:A30000")
Dim outputSh As Worksheet
Dim outputRng As Range
Set outputSh = Sheets("InStr")
Set outputRng = outputSh.Range("A1:A30000")
Dim time1 As Double, time2 As Double
time1 = MicroTimer
Dim arr As Variant
arr = inputRng
Dim validValues As String
validValues = "01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 'put numbers and capitals at the start as they are more likely'
Dim i As Integer, j As Integer
Dim result As String
For i = LBound(arr) To UBound(arr)
result = vbNullString
For j = 1 To Len(arr(i, 1))
If InStr(validValues, Mid(arr(i, 1), j, 1)) <> 0 Then
result = result & Mid(arr(i, 1), j, 1)
End If
Next j
arr(i, 1) = result
Next i
outputRng = arr
time2 = MicroTimer
Debug.Print (time2 - time1) * 1000
End Sub
3. Использование regex.Replace в массиве. Время: 171 мс
Определите регулярное выражение и используйте его для замены каждого элемента массива.
Sub Regex()
Dim inputSh As Worksheet
Dim inputRng As Range
Set inputSh = Sheets("Data")
Set inputRng = inputSh.Range("A1:A30000")
Dim outputSh As Worksheet
Dim outputRng As Range
Set outputSh = Sheets("Regex")
Set outputRng = outputSh.Range("A1:A30000")
Dim time1 As Double, time2 As Double
time1 = MicroTimer
Dim arr As Variant
arr = inputRng
Dim objRegex As Object
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Global = True
.ignorecase = True
.Pattern = "[^\w]"
End With
Dim i As Integer
For i = LBound(arr) To UBound(arr)
arr(i, 1) = objRegex.Replace(arr(i, 1), vbNullString)
Next i
outputRng = arr
time2 = MicroTimer
Debug.Print (time2 - time1) * 1000
End Sub
Edit:
@ThunderFrame - наши номера деталей обычно представлены в следующих форматах:
- Все номера (например, 32523452)
- Соединение букв и цифр (например, AB324K234 или 123H45645)
- Соединение букв и цифр, каждое из которых связано не-буквенно-цифровым символом (например, ABC001-001, ABC001/001, 123/4557-121).
Я думал об использовании regex.test для каждой строки перед запуском в замену, но я не уверен, что это просто скопирует строку, чтобы затем проверить ее, и в этом случае я могу просто сделать замену начните с.
@Slai - спасибо за ссылку - я рассмотрю это более подробно