Заключение переменной строки значений скобками в более крупную строку, содержащуюся в одной ячейке

У меня есть ячейки, которые содержат ! за которым следует буквенно-цифровая строка различной длины.

Я хочу изменить! на НЕ (что относительно просто, часть, с которой я борюсь, - это закрывающая скобка после буквенно-цифровой строки

т.е.

!AlphaNumericString становится NOT(AlphaNumericString)

Примеры ячеек до изменения,
И(!BI,ИЛИ(Z1,Z2,Z102,Z103,Z104,Z306))

!L1M23 ИЛИ (NP001 И !CA);
Z200 И !VP100;
!N001L010D132

Я хочу, чтобы эти клетки стали,
И(НЕ(BI),ИЛИ(Z1,Z2,Z102,Z103,Z104,Z306))

НЕ(L1M23) ИЛИ (NP001 И НЕ(CA));
Z200 И НЕ(VP100);
НЕ(N001L010D132)

За восклицательным знаком всегда следует 1 символ, тогда это будет серия буквенно-цифровых символов, и эта строка восклицания всегда будет заканчиваться одним из следующих символов:

Космос
)
;
,
Или это может закончиться последним символом в ячейке,

Согласно приведенным выше примерам

Надеюсь, это имеет смысл.

Я бы предпочел макрос, чтобы он был обратно совместим с EXCEL 365 до 2010 года, но если есть простая формула, это здорово.

Спасибо mbart67


1
96
4

Ответы:

Решено

но если есть простая формула, отлично

Не совсем, но в Office 365 мы можем комбинировать формулы, чтобы получить желаемый результат:

=MAP(A1:A2, 
 LAMBDA(a,
     REDUCE(a,SEQUENCE(LEN(a)-LEN(SUBSTITUTE(a,"!",))),
     LAMBDA(r,_,
LET(x,MID(r,FIND("!",r),LEN(r)),
    y,TOROW(HSTACK(FIND({" ","(",";",","},x),LEN(x)+1),2),
IFERROR(
        SUBSTITUTE(
                   REPLACE(r,
                           MIN(y+FIND("!",r)-1),
                           ,
                           ")"),
                   "!",
                   "NOT(",
                   1),
        r))))))

По сути, он проверяет, сколько раз ! встречается в исходной текстовой строке, чтобы установить количество итераций для сокращения, которое я объявил как _, поскольку оно используется для итераций, но больше нигде не вызывается внутри функции. Сама исходная текстовая строка объявляется как r.

Итерация Мы ищем первый найденный ! в строке r и находим ближайший ,),;,,-символ или конец строки после первого найденного ! (в зависимости от того, что наступит раньше), добавляем ) после него и заменяем этот первый счетчик. ! с NOT(. Если вышеперечисленные ошибки, сохраните r, если нет, замененный/замещенный r станет новым r и повторите итерацию _ раз.

Я использовал MAP, чтобы зациклить формулу на нескольких ячейках одновременно, но вы можете заменить часть MAP/LAMBDA на LET(a,A1,REDUCE(.., чтобы при необходимости она работала с одной ячейкой.

Если вы находитесь на бета-канале, вы можете использовать:

=REGEXREPLACE(A1:A2,"!(\w+)","NOT($1)")


Вы можете попробовать эту UDF (пользовательскую функцию) и использовать ее на листе как =myReplace(A1), где ячейка A1 содержит исходную строку.

Функция есть;

Function myReplace(myRange As Range) As String
    Dim regExp As Object
    
    Set regExp = CreateObject("VBScript.RegExp")
    
    regExp.IgnoreCase = True
    regExp.Global = True
    regExp.Pattern = "!([A-Z0-9]*)"
    
    myReplace = regExp.Replace(myRange.Text, "NOT($1)")

    Set regExp = Nothing
End Function


Решение без регулярных выражений:

Function exclamToNot(s As String) As String

    Dim found As Boolean
    Dim i As Integer
    Dim c As String
    Dim t As String
    
    Const terminators As String = ");, "
    
    
    For i = 1 To Len(s)
    
    c = Mid(s, i, 1)
    
 ' Check for exclamation mark
 
    If c = "!" Then
        found = True
        t = t & "not("
        
    ' Check for end of string controlled by exclamation mark
    
    Else
        If found And InStr(terminators, c) > 0 Then
            found = False
            t = t & ")" & c
            
        ' Normal character
        
        Else
            t = t & c
        End If
    End If
            
    Next i
    
    ' Add final bracket if we have reached end of string after exclamation mark
    
    If found Then t = t & ")"
    
    exclamToNot = t

End Function

Мне было интересно, можете ли вы использовать ту же самую логику в формуле листа. Кажется, это работает нормально:

=LET(
    c, MID(s, i, 1),
    terminators, ";,) ",
    IF(
        i > LEN(s),
        IF(found, ")", ""),
        IF(
            c = "!",
            "NOT(" & Repex(s, TRUE, i + 1),
            IF(
                AND(found, ISNUMBER(FIND(c, terminators))),
                ")" & c & Repex(s, FALSE, i + 1),
                c & Repex(s, found, i + 1)
            )
        )
    )
)

называется как

=repex(A1,false,1)

и сохранен как лямбда, поэтому в диспетчере имен это выглядит так:


Function ConvertString(s As String) As String
    With CreateObject(Class: = "VBScript.RegExp")
        .Pattern = "!([A-Z0-9]{1,})"
        .Global = True
        ConvertString = .Replace(s, "NOT($1)")
    End With
End Function