Безопасно и эффективно разобрать и заменить токены в строке


Я написал функцию на VBA, которая будет делать разбор / замена по маркерам / заполнителей.

Пример:
Входной строки: Username %u, date is %d.
Функция будет заменить %d С текущие дату и %u с текущим именем пользователя; выход будет выглядеть так:
Username Andy, date is 20170820.

Звучит просто реализовать, но вот поворот: регулярные выражения и заменить() не безопасны. В приведенном выше примере, если %u после замены будет содержать еще один знак (скажем Andy%d), будет рекурсивной замены и выход исковеркали: Username Andy20170820, date is 20170820.

Я мог бы написать это эффективно в C++ или другой "правильный" язык, но я отошла на языке VBA. Я не хочу работать на символы внутри строки, что выглядит не очень эффективной (и я мог бы использовать эту формулу для анализа 10000 строк в таблицу Excel).

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


Function AI_ParseMagicSymbols(ByVal TextToParse As String) As String

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'' Replace magic symbols (placeholders) with dynamic data.
''
'' Arguments: a string full of magic.
''
'' Placeholders consist of one symbol prepended with a %:
''    %d - current date
''    %t - current time
''    %u - username (user ID)
''    %n - full user name (usually name and surname)
''    %% - literal % (placeholder escape)
''    Using an unsupported magic symbol will treat the % literally, as if it had been escaped.
''    A single placeholder terminating the string will also be treated literally.
''    Magic symbols are case-sensitive.
''
'' Returns:   A string with no magic but with lots of beauty.
''
'' Examples:
'' "Today is %d" becomes "Today is 2018-01-26"
'' "Beautiful time: %%%t%%" yields "Beautiful time: %16:10:51%"
'' "There are %zero% magic symbols %here%.", true to its message, outputs "There are %zero% magic symbols %here%."
'' "%%% looks lovely %%%" would show "%% looks lovely %%" - one % for the escaped "%%" and the second one for the unused "%"!
''
'' Alexander Ivashkin, 26 January 2018
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim sFinalResult As String
Dim aTokenizedString() As String
Dim sTempString As String
Dim sPlaceholder As String
Dim sCurrentString As String
Dim iIterator As Integer
Dim iTokenizedStringSize As Integer
Dim bThisStringHasPlaceholder As Boolean

' Default placeholder is "%"
Const cPlaceholderSymbol As String = "%"

aTokenizedString = Split(Expression:=TextToParse, Delimiter:=cPlaceholderSymbol)
iTokenizedStringSize = UBound(aTokenizedString())
bThisStringHasPlaceholder = False
sFinalResult = ""

For iIterator = 0 To iTokenizedStringSize
    sCurrentString = aTokenizedString(iIterator)

    If bThisStringHasPlaceholder Then
        If sCurrentString <> "" Then
            sPlaceholder = Left(sCurrentString, 1)
            sTempString = Right(sCurrentString, Len(sCurrentString) - 1)

            ' This is the place where the MAGIC happens
            Select Case sPlaceholder
                Case "d":
                    sCurrentString = Date & sTempString
                Case "t":
                    sCurrentString = Time & sTempString
                Case "u":
                    sCurrentString = Environ$("Username") & sTempString
                Case "n":
                    sCurrentString = Environ$("fullname") & sTempString
                Case Else:
                    sCurrentString = cPlaceholderSymbol & sCurrentString
            End Select
        Else
            ' We had two placeholders in a row, meaning that somebody tried to escape!
        sCurrentString = cPlaceholderSymbol
        bThisStringHasPlaceholder = False
    End If
End If

sFinalResult = sFinalResult & sCurrentString

If sCurrentString = "" Or (iIterator + 1 <= iTokenizedStringSize And sCurrentString <> cPlaceholderSymbol) Then
    ' Each string in the array has been split at the placeholders. If we do have a next string, then it must contain a magic symbol.

    bThisStringHasPlaceholder = True
    ' Even though it is called "...ThisString...", it concerns the NEXT string.
    ' The logic is correct as we will check this variable on the next iteration, when the next string will become ThisString.
Else
    bThisStringHasPlaceholder = False
End If

Next iIterator

AI_ParseMagicSymbols = sFinalResult

End Function


170
3
задан 26 января 2018 в 09:01 Источник Поделиться
Комментарии
1 ответ

Мои ближайшие идеи, не глядя на ваш код, чтобы использовать split. Я пытался оптимизировать свой код, используя некоторые хитрости:


  • Чтобы избавиться от проблем с двуспальной %, я заменить этот манекен-строку (которая не появится в ваш вклад, надеемся, что вы можете принять это).

  • Я оставляю только первый знак. Или входной начинается с % - в этом случае она пустая, так или иначе, этого не произошло, поэтому мы не должны смотреть на это на все.

  • В конце концов, Незнайка-строка заменяется один %

Я в конечном итоге с:

Function AI_ParseMagicSymbols(ByVal s As String) As String
Const PlaceholderSymbol As String = "%"
Const ReplaceEscapedChar As String = "<ESCAPEPERCENT>"

s = Replace(s, PlaceholderSymbol & PlaceholderSymbol, ReplaceEscapedChar)
Dim tokens() As String, i As Long
tokens = Split(s, PlaceholderSymbol)

AI_ParseMagicSymbols = tokens(0) ' We don't treat the first token
For i = 1 To UBound(tokens)
Dim token As String, placeHolderChar As String, replaceStr As String
token = tokens(i)
placeHolderChar = Left(token, 1)

' This is the place where the MAGIC happens
Select Case placeHolderChar
Case "d":
replaceStr = Date
Case "t":
replaceStr = Time
Case "u":
replaceStr = Environ$("Username")
Case "n":
replaceStr = Environ$("fullname")
Case Else:
replaceStr = "%" & placeHolderChar ' No magic here, keep % and the first char of token
End Select

AI_ParseMagicSymbols = AI_ParseMagicSymbols & replaceStr & Mid(token, 2)
Next i

AI_ParseMagicSymbols = Replace(AI_ParseMagicSymbols, ReplaceEscapedChar, PlaceholderSymbol)

End Function

1
ответ дан 7 февраля 2018 в 05:02 Источник Поделиться