У меня есть ячейки, которые содержат ! за которым следует буквенно-цифровая строка различной длины.
Я хочу изменить! на НЕ (что относительно просто, часть, с которой я борюсь, - это закрывающая скобка после буквенно-цифровой строки
т.е.
!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
но если есть простая формула, отлично
Не совсем, но в 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)
и сохранен как лямбда, поэтому в диспетчере имен это выглядит так: