虽然您的请求可以通过一些非常复杂(且 CPU 密集型)公式仅使用原生 Excel 函数来实现,但 VBA 用户定义函数 或 UDF 将非常有用更合适。我不是化学家,所以请原谅我提供的对您的单个样品的添加,因为它们是从Internet page 无耻地偷走的。 TBH,我什至不确定我是否有一半的术语正确。
第 1 步 - 创建一个分子量表并命名
您将需要某种形式的交叉引用来从元素的周期符号中检索分子量。这是我拼凑的。我将在下面的示例工作簿中提供指向完整数据表的链接。
在名为元素数据的工作表上,转到Formulas ► Defined Names ► Name Manger 并为交叉引用矩阵指定一个定义的名称。
在这里,我使用了一个公式 (=OFFSET('Element Data'!$A$1,0,0,COUNTA( 'Element Data'!$A:$A),6)) 来定义范围,但数据的大小是相当静态的,因此单元格范围引用应该绰绰有余。
第 2 步 - 添加用户定义函数的代码
点击Alt+F11,当VBE打开时,立即使用下拉菜单Insert ► Module (Alt+I+M)。将以下内容粘贴到标题为 Book1 - Module1 (Code) 的新窗格中。
Public Function udf_Molecular_Weight(sCMPND As String) As Double
Dim sTMP As String, i As Long, sEL As String, sSB As String
Dim dAW As Double, dAWEIGHT As Double, dSUB As Long
sTMP = sCMPND: dAWEIGHT = 0: sSB = "0": sEL = vbNullString
Do While CBool(Len(sTMP))
sSB = "0": sEL = vbNullString
If Asc(Mid(sTMP, Application.Min(2, Len(sTMP)), 1)) > 96 Then
sEL = Left(sTMP, 2)
Else
sEL = Left(sTMP, 1)
End If
sTMP = Right(sTMP, Len(sTMP) - Len(sEL))
Do While IsNumeric(Left(sTMP, 1))
sSB = sSB & Int(Left(sTMP, 1))
sTMP = Right(sTMP, Len(sTMP) - 1)
Loop
'Debug.Print sEL & ":" & (Int(sSB) - (Not CBool(Int(sSB))))
dAWEIGHT = dAWEIGHT + Application.VLookup(sEL, ThisWorkbook.Names("tblPeriodic").RefersToRange, 6, False) * (Int(sSB) - (Not CBool(Int(sSB))))
Loop
udf_Molecular_Weight = dAWEIGHT
End Function
Public Function udf_Styled_Formula_Alt(sCMPND As String) As String
Dim sb As Long, sCOMPOUND As String
sCOMPOUND = sCMPND
For sb = 0 To 9
sCOMPOUND = Replace(sCOMPOUND, sb, ChrW(8320 + sb))
Next sb
udf_Styled_Formula_Alt = sCOMPOUND
End Function
Public Function udf_Unstyled_Formula_Alt(sCMPND As String) As String
Dim sb As Long, sCOMPOUND As String
sCOMPOUND = sCMPND
For sb = 0 To 9
sCOMPOUND = Replace(sCOMPOUND, ChrW(8320 + sb), sb)
Next sb
udf_Unstyled_Formula_Alt = sCOMPOUND
End Function
只有第一个与您发布的问题相关。后两者风格化化合物的化学式用Unicode下标字符并逆向处理。
完成粘贴后,点击 Alt+Q 返回工作表。这些 UDF 函数现在可以在您的工作簿中使用,就像任何本机 Excel 函数一样。语法尽可能简单。
=udf_Molecular_Weight()
对于您的示例化合物(在上面的数据图像中),这将是,
=udf_Molecular_Weight(B2)
...或者,
=udf_Molecular_Weight("C2H3N")
其中有 9000 多个,我怀疑您会使用前一种方法。根据需要填写。虽然这个 UDF 比使用 INDIRECT 和其他本机工作表函数的复杂 array 公式要高效得多,但它们并不神奇。在提交到 9000+ 之前,在几百行上测试公式,这样你就知道会发生什么。如果您选择使用其他两个 UDF,它们的工作方式大致相同。
简要说明:
'变量声明',我猜你实际上是指'变量赋值'。我倾向于编写相当紧凑的代码,并且通过将变量的归零与冒号堆叠在一起,我将其他人最多可以放入 4 行代码行中的内容放在一行中。我转这个,
sTMP = sCMPND
dAWEIGHT = 0
sSB = "0"
sEL = vbNullString
...进入这个,
sTMP = sCMPND: dAWEIGHT = 0: sSB = "0": sEL = vbNullString
在重新进入循环之前需要重置变量,但这是一项平凡的任务,所以我只是将所有四个分配都塞进一行。
两个Do While ... Loop逐个字符地爬取传入函数的字符串。内部循环专门处理数字。每次通过循环都会从左侧截断字符串,将其缩短一个或多个字符,并将这些字符收集为元素的符号或与其在有机化合物中的使用相关的数字。最终没有任何东西可以截断(长度=0),这就是CBool(Len(sTMP)) 变为 False 并且循环结束的地方。内部循环的执行方式大致相同,但会收集数字直到它没有长度或字母字符。在收集到一个元素(和可能的数字修饰符)后,化合物中该元素的分子量使用VLOOKUP 对照分子量表计算,并添加到一个不断增长的数字中。当所有元素及其相关数量已收集并添加到总计中时,总计将作为函数的结果返回。