我的单元格包含一个!,后跟长度不一的字母数字字符串。
我想将 ! 更改为 NOT(,这相对容易,我遇到困难的部分是字母数字字符串后的右括号
IE
!AlphaNumericString 变为NOT(AlphaNumericString )
更改前的单元格示例,
AND( !BI ,OR(Z1,Z2,Z102,Z103,Z104,Z306))
!L1M23或 (NP001 和!CA );
Z200 和!VP100 ;
!N001L010D132
我希望这些单元格变成
AND( NOT(BI),OR(Z1,Z2,Z102,Z103,Z104,Z306))
非 (L1M23)或 (NP001 且非 (CA) );
Z200 且非 (VP100) ;
非 (N001L010D132)
Exclamation 后面总是跟着 1 个字符,然后它将是一系列字母数字字符,并且 Exclamation 字符串将始终以下列字符之一结尾
空格
)
;
,
或者以单元格中的最后一个字符结尾,
根据上述例子
希望这是有意义的。
我更喜欢宏,这样它可以从 EXCEL 365 向后兼容到 2010,但如果有简单的公式,那就太棒了。
谢谢 mbart67
1
6 个回答
6
您可以尝试这个 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
4
-
3并且它按照 OP 的标签在 Excel 2010 中运行。
– -
虽然这可能并不重要,但 VBScript.RegExp 在 Mac 上不可用。
– -
1@nkalvi,然后他可以使用跨浏览器和操作系统的 Google Sheets……
=REGEXREPLACE(A1,"!([A-Z0-9]+)","NOT($1)")
–
-
你说得对。我想提一下,因为它在我的 Mac 上不起作用。多亏了你的回答,我现在知道如何在旧版本的 Excel 中(在 Windows 下)获取正则表达式功能。
–
|
非正则表达式解决方案:
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)
并保存为 lambda,因此在名称管理器中看起来像这样:
6
-
递归版本非常好!几乎可以处理
!(OR(A-B, !(C-D)), E)
=>NOT((OR(A-B), NOT((C-D))), E)
– -
谢谢!我现在要拼命努力让嵌套案例发挥作用了。
– -
Tom,谢谢你的帮助,你提供的函数 (exclamToNot) 非常好用。我还找人用正则表达式做了这件事,如果你知道它是如何工作的,那么就很简单了,下面提供了解决方案。
–
-
1感谢@TomSharpe 的启发,尽管我的尝试很粗糙,但我还是忍不住尝试 🙂
–
-
LAMBDA(s,found,i, ) 对于 LET 函数的运行至关重要,但之后它就起作用了。我喜欢这种方法。
–
|
但如果有简单的公式,那就太棒了
事实并非如此,但在 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))))))
基本上,它会检查在初始文本字符串中找到的次数,!
以设置 Reduce 的迭代次数,我将其声明为,因为_
它用于迭代,但在函数中不再调用。初始文本字符串本身声明为r
。
迭代!
我们在字符串中
搜索第一个找到的字符r
,并找到最接近的
、)
、;
、,
字符或第一个找到的字符之后的字符串结尾!
(以先到者为准),在)
其后面添加 ,并将第一个计数替换!
为NOT(
。如果上述错误,则保留r
,否则,被替换/替代的字符r
将成为新的r
,并重复迭代_
次数。
我曾经MAP
一次将公式循环到多个单元格,但如果需要,您可以替换 -partMAP/LAMBDA
以LET(a,A1,REDUCE(..
使其在单个单元格上起作用。
如果您使用的是 Beta 频道,则可以使用:
=REGEXREPLACE(A1:A2,"!(\w+)","NOT($1)")
4
-
可能有使用 REGEXREPLACE 的更简单的方法,但这仍处于测试阶段。
– -
谢谢,是的,这有效,我永远也不会得到这个,因为我仍然不理解所有这些公式,但我可以看到它试图做什么。是否可以将其写成宏函数,这样我就不必在需要时输入所有内容?谢谢 Heaps,效果很好 🙂
–
-
记录为宏并作为值写掉?
– -
我还添加了 REGEXREPLACE 选项,以防您可以访问它(或者它可能在不久的将来可用)。
–
|
尝试使用递归,尝试处理嵌套:
在名称管理器中定义exclaim_not
:
=LAMBDA(str, IF(ISNUMBER(FIND("!", str)), LET(delims, {"("," ",",",";",")"}, left_, LEFT(str, FIND("!", str) - 1), right_, exclaim_not(RIGHT(str, LEN(str) - LEN(left_) - 1)), symbol_len, MIN(IFERROR(SEARCH(delims, right_), LEN(right_) + 1)) - 1, symbol, LEFT(right_, symbol_len), after_symbol, RIGHT(right_, LEN(right_) - LEN(symbol)), left_ & "NOT" & IF(AND(LEFT(after_symbol) = "(", RIGHT(after_symbol) = ")"), IF(LEN(symbol), "(" & symbol & after_symbol & ")", after_symbol), "(" & symbol & ")" & after_symbol)), str))
格式化:
=LAMBDA(str,
IF(
ISNUMBER(FIND("!", str)),
LET(
delims, {"(", " ", ",", ";",")"},
left_, LEFT(str, FIND("!", str) - 1),
right_, exclaim_not(RIGHT(str, LEN(str) - LEN(left_) - 1)),
symbol_len, MIN(IFERROR(SEARCH(delims, right_), LEN(right_) + 1)) - 1,
symbol, LEFT(right_, symbol_len),
after_symbol, RIGHT(right_, LEN(right_) - LEN(symbol)),
left_ & "NOT" &
IF(
AND(LEFT(after_symbol) = "(", RIGHT(after_symbol) = ")"),
IF(LEN(symbol), "(" & symbol & after_symbol & ")", after_symbol),
"(" & symbol & ")" & after_symbol
)
),
str
)
)
2
-
这真的很棒,应该引起更多关注。也许我会尝试对它或类似的东西进行逆向工程,将其放入 VBA 中(也许)。它还展示了 stackoverflow 应该如何工作。
– -
您的反馈非常宝贵 – 谢谢!我几十年前就用过 VB,很少用 VBA。现在更喜欢用 JS 和 Swift 🙂
–
|
Like
使用否定模式替代正则表达式
这种方法不需要逐个检查并将每个字符添加到整个字符串中,
- 通过相应的分隔符将每行代码拆分
"!"
成标记, varStr
通过辅助函数识别每个标记的起始字母数字变量字符串getVarStr()
,Enclose()
通过函数执行简单的替换- 将修改后的标记加入到最终结果中。
示例调用
Sub EncloseExclam()
'Purp: Replace "!" and following variable alpha-num string part with surrounding elements
With Sheet1.Range("A2:A5") ' << redefine as needed
Dim data: data = .Value2
Dim i As Long
For i = LBound(data) To UBound(data)
data(i, 1) = enclose(data(i, 1)) ' << Function enclose()
Next i
.Offset(, 1) = data
End With
End Sub
函数enclose()
Function enclose(ByVal s As String) As String
'Purp.: Execute "!"-replacement by given prefix+"(" ENCL and closing bracket ENCL2
'0) Define constants
Const REPLACECHAR = "!", ENCL = "NOT(", ENCL2 = ")"
'1) Split code into tokens
Dim tokens: tokens = Split(s, REPLACECHAR)
'2) Loop through all "!"-split tokens
Dim i As Long
For i = 1 To UBound(tokens) ' i=1, as very first index i=0 can't contain "!"
'Identify variable alphanum string
Dim varStr As String: varStr = getVarStr(tokens(i))
tokens(i) = Replace(tokens(i), varStr, ENCL & varStr & ENCL2)
Next i
'3) Join tokens as function result
enclose = Join(tokens, "")
End Function
辅助函数 getVarStr()
通过负面 模式使用负面字符检查,例如(注意之前的"[!A-Z0-9]"
感叹号,即识别第一个非字母数字字符)。!
A-Z0-9
另一个好处是,可以传递一个可选参数,UCaseOnly:=False
以允许变量字符串内使用小写字符。
Function getVarStr(ByVal s As String, Optional UCaseOnly As Boolean = True) As String
'Purp: get starting alphanumeric string part
Dim Pattern As String
Pattern = IIf(UCaseOnly, "[!A-Z0-9]", "[!A-Za-z0-9]")
Dim i As Long
For i = 1 To Len(s)
If Mid(s, i, 1) Like Pattern Then
s = Left(s, i - 1): Exit For
End If
Next i
getVarStr = s
End Function
|
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
3
-
感谢您为 Stack Overflow 社区做出贡献。这可能是一个正确答案,但提供代码的额外解释以便开发人员了解您的推理确实很有用。这对于不太熟悉语法或难以理解概念的新开发人员尤其有用。您能否您的答案以包含更多详细信息以造福社区?
– -
代码如果附有解释会更有帮助。Stack Overflow 旨在提供学习,而不是提供代码片段供盲目复制粘贴。请您的答案并解释它如何回答所提出的具体问题。请参阅。
– -
好的,但非常接近@Haluk 的解决方案:-;
–
|
–
|