9

抱歉,如果这已经被问到并回答了,但我找不到令人满意的答案。

我有一个化学式列表,按以下顺序排列:C、H、N 和 O。我想在每个字母后面加上数字。问题是并非所有公式都包含 N。然而,所有公式都包含 C、H 和 O。数字可以是单数、双数或(仅在 H 的情况下)三位数。

因此数据看起来像这样:

  • C 20 H 37 N 1O5
  • C 10 H 12 O 3
  • C 20 H 19 N 3 O 4
  • C 23 H 40 O 3
  • C 9 H 13 N 1 O 3
  • C 14 H 26 O 4
  • C 58 H 100 N 2 O 9

我希望列表中的每个元素编号在单独的列中。所以在第一个例子中它将是:

20 37 1 5

我一直在尝试:

=IFERROR(MID(LEFT(A2,FIND("H",A2)-1),FIND("C",A2)+1,LEN(A2)),"") 

分离出C#。然而,在这之后我被卡住了,因为 H# 的两侧是 O 或 N。

有没有可以做到这一点的excel公式或VBA?

4

6 回答 6

13
于 2017-09-07T08:48:42.620 回答
4

This seems to work just fine:

enter image description here

Formula in B2 is below. Drag across and down

=IFERROR(IFERROR(--(MID($A2,SEARCH(B$1,$A2)+1,3)),IFERROR(--(MID($A2,SEARCH(B$1,$A2)+1,2)),--MID($A2,SEARCH(B$1,$A2)+1,1))),0)

Or a shorter array formula, which must be entered with ctrl+shift+enter

=MAX(IFERROR(--MID($A2,SEARCH(B$1,$A2)+1,ROW($A$1:$A$3)),0))

If you wanted to keep the VBA super simple, something like this works as well:

Public Function ElementCount(str As String, element As String) As Long
    Dim i As Integer
    Dim s As String

    For i = 1 To 3
        s = Mid(str, InStr(str, element) + 1, i)
        On Error Resume Next
        ElementCount = CLng(s)
        On Error GoTo 0
    Next i
End Function

Use it like so:

=ElementCount(A1,"C")
于 2017-09-07T13:49:31.477 回答
2

我在 VBA 中使用正则表达式进行了此操作。您也可以像 Vityata 建议的那样通过循环遍历字符串来做到这一点,但我怀疑这会稍微快一些并且更容易阅读。

Option Explicit

Function find_associated_number(chemical_formula As Range, element As String) As Variant
  Dim regex As Object: Set regex = CreateObject("VBScript.RegExp")
  Dim pattern As String
  Dim matches As Object

  If Len(element) > 1 Or chemical_formula.CountLarge <> 1 Then
    find_associated_number = CVErr(xlErrName)
  Else
    pattern = element + "(\d+)\D"
    With regex
      .pattern = pattern
      .ignorecase = True
      If .test(chemical_formula) Then
        Set matches = .Execute(chemical_formula)
        find_associated_number = matches(0).submatches(0)
      Else
        find_associated_number = CVErr(xlErrNA)
      End If
    End With
  End If
End Function

然后像往常一样在工作表中使用公式:

在此处输入图像描述

C 列包含碳原子数,D 列包含氮原子数。只需通过复制公式并更改它搜索的元素来扩展它。

于 2017-09-07T08:45:09.257 回答
1

使用 VBA,这是一项简单的任务 - 您必须遍历字符并检查值是否为数字。使用 Excel,该解决方案包括一些冗余。但这是可行的。例如,

如果应用以下公式,C20H37NO5将返回20375 :

=IF(ISNUMBER(1*MID(A1,1,1)),MID(A1,1,1),"")&
IF(ISNUMBER(1*MID(A1,2,1)),MID(A1,2,1),"")&
IF(ISNUMBER(1*MID(A1,3,1)),MID(A1,3,1),"")&
IF(ISNUMBER(1*MID(A1,4,1)),MID(A1,4,1),"")&
IF(ISNUMBER(1*MID(A1,5,1)),MID(A1,5,1),"")&
IF(ISNUMBER(1*MID(A1,6,1)),MID(A1,6,1),"")&
IF(ISNUMBER(1*MID(A1,7,1)),MID(A1,7,1),"")&
IF(ISNUMBER(1*MID(A1,8,1)),MID(A1,8,1),"")&
IF(ISNUMBER(1*MID(A1,9,1)),MID(A1,9,1),"")

目前,它检查前 9 个字符是否为数字。如果要包含超过 9 个,则只需在公式中添加几行即可。

公式中有一个小技巧 - 1*。如果可能,它会将文本字符转换为数字。因此,a5作为文本,乘以1变为数字字符。

于 2017-09-07T08:29:33.440 回答
1

使用 split 和 like 方法。

Sub test()
    Dim vDB As Variant, vR() As Variant
    Dim s As String
    Dim vSplit As Variant
    Dim i As Long, n As Long, j As Integer

    vDB = Range("a2", Range("a" & Rows.Count).End(xlUp))

    n = UBound(vDB, 1)
    ReDim vR(1 To n, 1 To 4)
    For i = 1 To n
        s = vDB(i, 1)
        For j = 1 To Len(s)
            If Mid(s, j, 1) Like "[A-Z]" Then
                s = Replace(s, Mid(s, j, 1), " ")
            End If
        Next j
        vSplit = Split(s, " ")
        For j = 1 To UBound(vSplit)

            vR(i, j) = vSplit(j)
        Next j
    Next i
    Range("b2").Resize(n, 4) = vR
End Sub
于 2017-09-07T08:42:28.427 回答
1

如果您想要一个 vba 解决方案来提取所有数字,我首选的解决方案是使用正则表达式。以下代码将从字符串中提取所有数字

Sub GetMolecularFormulaNumbers()
    Dim rng As Range
    Dim RegExp As Object
    Dim match, matches
    Dim j As Long

    Set rng = Range(Cells(1, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row, 1))
    Set RegExp = CreateObject("vbscript.regexp")
    With RegExp
        .Pattern = "\d+"
        .IgnoreCase = True
        .Global = True

        For Each c In rng
            j = 0
            Set matches = .Execute(c)
            If matches.Count > 0 Then
                For Each match In matches
                    j = j + 1
                    c.Offset(0, j) = CInt(match)
                Next match
            End If
        Next c
    End With
End Sub
于 2017-09-07T08:45:50.360 回答