Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Microsoft Office и VBA программирование > Microsoft Office Word
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 10.02.2013, 07:45   #1
Скрипт
Форумчанин
 
Регистрация: 24.12.2012
Сообщений: 776
По умолчанию Как узнать, сколько в тексте пробелов

Допустим макрос нашёл нужный текст в документе.
Как правильнее проанализировать этот текст на количество пробелов?

Есть три варианта:
Replace;
Split;
Цикл + InStr.

Если найден большой фрагмент документа, например, в 1000 символов, то ведь функция Replace создаст в оперативной памяти компьютера новую строку, состоящую из 1000 символов, но без пробелов:
Len(Replace(Текст, " ", ""))

"Split" можно так использовать:
UBound(Split(Текст, " ", 14))
14 позволяет создавать небольшой массив, что ускорит работу кода, чем создавать массив на основе всех пробелов.

Мне кажется, что самое оптимальное - это Цикл + InStr, но это надо усложнять код: добавлять лишний цикл, дополнительные переменные делать.

Кто и что думает?
Скрипт вне форума Ответить с цитированием
Старый 10.02.2013, 10:16   #2
viter.alex
Балуюсь кодами
Участник клуба
 
Аватар для viter.alex
 
Регистрация: 09.01.2009
Сообщений: 1,837
По умолчанию

Я всегда пользуюсь Replace, поскольку самый явный способ. 1000 символов это получается 2кб. Это много?
Проверил на практике:
Для Replace:
Цитата:
Длина строки: 25529. Количество пробелов: 3395
Длительность: 0,0034736004 секунд.
Для Split:
Цитата:
Длина строки: 25529. Количество пробелов: 3395
Длительность: 0,0032005573 секунд.
Оказывается, Split работает быстрее, что для меня неожиданно. Но разница в долях микросекунд.
Код:
Option Explicit
'структура для хранения 64-разрядного числа
 Private Type LARGE_INTEGER
     LowPart As Long
     HighPart As Long
 End Type

 'апи функция для получения значения счетчика
 Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long

 'апи функция для получения значения частоты
 Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long

 'апи функция для копирования области памяти
 Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Sub TestMethods()
     Dim T As Long, liFrequency As LARGE_INTEGER, liStart As LARGE_INTEGER, liStop As LARGE_INTEGER
     Dim cuFrequency As Currency, cuStart As Currency, cuStop As Currency
     Dim n As Long, s As String
     s = ActiveDocument.Range.Text
     'получить частоту счетчика производительности
     If QueryPerformanceFrequency(liFrequency) = 0 Then
        MsgBox "Ваше аппаратное обеспечение не поддерживает счетчик производительности высокой точности!", vbInformation
     Else
        'конвертировать 64-разрядное число в тип currency
        cuFrequency = LargeIntToCurrency(liFrequency)
        
        'получить количество "тиков"
        QueryPerformanceCounter liStart
        
        'НАЧАЛО ТЕСТИРУЕМОГО КОДА
'        n = Len(s) - Len(Replace(s, " ", ""))
        n = UBound(Split(s, " "))
        'КОНЕЦ ТЕСТИРУЕМОГО КОДА
        
        'получить количество "тиков"
        QueryPerformanceCounter liStop
        
        'конвертировать 64-разрядное число в тип currency
        cuStart = LargeIntToCurrency(liStart)
        cuStop = LargeIntToCurrency(liStop)
        
        'вычислить как много времени затрачено, и показать результат
        Debug.Print "Длина строки: " & Len(s) & ". Количество пробелов: " & n & vbCr & _
                    "Длительность: " & CStr(Round((cuStop - cuStart) / cuFrequency, 10)) & " секунд."
     End If
End Sub

 'функция для конвертирования 64-разрядного числа в тип currency
 Private Function LargeIntToCurrency(liInput As LARGE_INTEGER) As Currency
     'копировать 8 байт из 64-разрядного в currency
     CopyMemory LargeIntToCurrency, liInput, LenB(liInput)

     'adjust it - хз =)
     LargeIntToCurrency = LargeIntToCurrency * 10000
 End Function
Лучше день потерять — потом за пять минут долететь!©

Последний раз редактировалось viter.alex; 10.02.2013 в 14:22.
viter.alex вне форума Ответить с цитированием
Старый 10.02.2013, 13:31   #3
nerv
Форумчанин
 
Аватар для nerv
 
Регистрация: 26.04.2010
Сообщений: 450
По умолчанию

Цитата:
Сообщение от Скрипт Посмотреть сообщение
Если найден большой фрагмент документа, например, в 1000 символов
1000 символов - это небольшой фрагмент

Цитата:
Сообщение от Скрипт Посмотреть сообщение
Мне кажется, что самое оптимальное - это Цикл + InStr, но это надо усложнять код: добавлять лишний цикл, дополнительные переменные делать.
вынеси в отдельную функцию

Код:
If ChrCount(text, Chr, MaxCount) Then

' ...

Function ChrCount(Byref text, _
                          Byval chr, _
                          Optional Byval maxCount ) As Boollean
    ' some code
    ChrCount = true
End function
Ты пишешь ее один раз и таскаешь из проекта в проект.
Тишина – самый громкий звук
nerv вне форума Ответить с цитированием
Старый 10.02.2013, 13:35   #4
Казанский
Старожил
 
Аватар для Казанский
 
Регистрация: 31.12.2010
Сообщений: 2,133
По умолчанию

Если время критично, то, конечно, не следует преобразовывать строку - ни в массив, ни в другую строку без пробелов.
Можно сравнивать каждый символ строки с пробелом. Через Mid$(s,i,1)=" " это долго, лучше копировать двоичное содержимое символа в переменную типа Integer и сравнивать с константой того же типа:
Код:
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Function SpacesInString(s$) As Long
Dim a%, i&
For i = StrPtr(s) To StrPtr(s) + Len(s) * 2 - 2 Step 2
    CopyMemory a, ByVal i, 2&
    If a = 32 Then SpacesInString = SpacesInString + 1&
Next
End Function
Выигрыш примерно в 3 раза
Код:
Длина строки: 2268. Количество пробелов: 835 - Split
Длительность: 0,0006366731 секунд.
Длина строки: 2268. Количество пробелов: 835 - Replace
Длительность: 0,0006632128 секунд.
Длина строки: 2268. Количество пробелов: 835 - SpacesInString
Длительность: 0,0002234921 секунд.
exceleved@yandex.ru Яндекс.Деньги: 410011500007619
Казанский вне форума Ответить с цитированием
Старый 10.02.2013, 17:27   #5
nerv
Форумчанин
 
Аватар для nerv
 
Регистрация: 26.04.2010
Сообщений: 450
По умолчанию

Цитата:
Сообщение от Казанский Посмотреть сообщение
Выигрыш примерно в 3 раза
влом писать, но осмелюсь предположить, что вариант предложенный мной будет работать еще быстрей, т.к. там есть параметр maxCount, достигнув которого можно перестать считать и выйти

+ никаких внешних функций

только я там затупил, ф-я должна возвращать кол-во пробелов
Тишина – самый громкий звук

Последний раз редактировалось nerv; 10.02.2013 в 17:30.
nerv вне форума Ответить с цитированием
Старый 10.02.2013, 23:37   #6
Казанский
Старожил
 
Аватар для Казанский
 
Регистрация: 31.12.2010
Сообщений: 2,133
По умолчанию

Мне кажется, что самое оптимальное - это Цикл + InStr, но это надо усложнять код: добавлять лишний цикл, дополнительные переменные делать.
Да, это быстрее. И совсем не сложно
Код:
Function CntInstr(s$) As Long
Dim i&
Do
    i = InStr(i + 1, s, " ")
    If i Then CntInstr = CntInstr + 1 Else Exit Function
Loop
End Function
exceleved@yandex.ru Яндекс.Деньги: 410011500007619
Казанский вне форума Ответить с цитированием
Старый 11.02.2013, 00:50   #7
Sasha_Smirnov
Особый статус
Участник клуба
 
Аватар для Sasha_Smirnov
 
Регистрация: 24.11.2008
Сообщений: 1,535
По умолчанию

Цитата:
Сообщение от Скрипт Посмотреть сообщение
...макрос нашёл нужный текст в документе.
Как правильнее проанализировать этот текст на количество пробелов? Кто и что думает?
Моя мысль такая: вставить текст в новый документ и запустить подпрограмму (она оперирует готовыми методами документа Word).
Код:
Sub BlankQuantity()
Dim a, b
    a = ActiveDocument.ComputeStatistics( _
        wdStatisticCharactersWithSpaces, IncludeFootnotesAndEndnotes)
        
    b = ActiveDocument.ComputeStatistics( _
        wdStatisticCharacters, IncludeFootnotesAndEndnotes)
        
    MsgBox "В документе " & ActiveDocument & " найдено пробелов: " & a - b & "."
End Sub
Кстати, в текстах встречается и неразрывный пробел (юникод 0160), и табуляция. Это здесь учтено:
Изображения
Тип файла: jpg spaces-in-the-doc.jpg (41.2 Кб, 132 просмотров)
Sasha_Smirnov вне форума Ответить с цитированием
Старый 11.02.2013, 09:47   #8
Вождь
Форумчанин
 
Аватар для Вождь
 
Регистрация: 29.09.2008
Сообщений: 378
По умолчанию

Цитата:
Сообщение от Sasha_Smirnov Посмотреть сообщение
...в новый документ...
Как раз хотел напомнить о ComputeStatistics. А зачем новый документ? Можно прямо на колене:
Код:
    With Selection.Range
        MsgBox .ComputeStatistics(wdStatisticCharactersWithSpaces) - _
               .ComputeStatistics(wdStatisticCharacters)
    End With
Макросы на заказ и готовый пакет - http://mtdmacro.ru/
Вождь вне форума Ответить с цитированием
Старый 11.02.2013, 10:57   #9
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

всё гениальное просто!!!!!!!!!
Ципихович Эндрю вне форума Ответить с цитированием
Старый 11.02.2013, 11:22   #10
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

скажите я правильно закомментировал
Код:
'узнать, сколько выделено знаков
MsgBox$ Selection.Range.ComputeStatistics(wdStatisticCharactersWithSpaces)
'узнать, сколько выделено знаков без пробела
MsgBox$ Selection.Range.ComputeStatistics(wdStatisticCharacters)

Последний раз редактировалось Ципихович Эндрю; 11.02.2013 в 21:09.
Ципихович Эндрю вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как узнать сколько строк вывелось в DBGrid? Женя32 БД в Delphi 4 15.01.2013 22:46
Как узнать сколько раз просматривался файл vovanblch Безопасность, Шифрование 15 02.05.2011 01:15
как узнать сколько кликов было по кнопке . s1s1s1 Общие вопросы Delphi 5 13.02.2011 22:47
[PHP] Как узнать сколько прошло времени? RESPECT8 PHP 1 24.10.2010 09:43
замена пробелов в тексте designer999 Общие вопросы Delphi 13 25.02.2010 16:50