VBA宏逐个单元格复制粘贴

huangapple go评论76阅读模式
英文:

VBA macro cell by cell copy paste

问题

我希望你一切都好。

为了我的工作,我制作了一个VBA宏,它的功能是在单元格包含特定公式时将单元格的内容复制并粘贴为数值。

以下是宏的一部分摘录:

Option Explicit

Sub copyPasteValue()

Dim cellContent As String

Dim endRow As Integer
Dim endCol As Integer
Dim r As Integer
Dim c As Integer

Dim activeCellColumn As Integer
Dim activeCellRow As Integer

activeCellColumn = ActiveCell.Column
activeCellRow = ActiveCell.Row

endRow = Cells(Rows.Count, activeCellColumn).End(xlUp).Row
endCol = Cells(activeCellRow, Columns.Count).End(xlToLeft).Column

For c = activeCellColumn To endCol

    For r = activeCellRow To endRow

        cellContent = Cells(r, c).Formula

        If InStr(1, cellContent, "GetCtData") Then
           Cells(r, c).Copy
           Cells(r, c).PasteSpecial Paste:=xlPasteValues
        End If

    Next

Next

Cells(activeCellRow, activeCellColumn).Select

If ActiveWorkbook.Name = "VALUE.xlsx" Then
    ActiveWorkbook.Save
Else
    ActiveWorkbook.SaveAs Filename:="VALUE"
End If

End Sub

这个宏运行速度非常慢,对于某些工作表可能需要超过1小时。有没有人有办法提高速度?

提前感谢你的帮助。

英文:

I hope you are doing great.

For my work I made a VBA macro which consist on copy pasting in value the content of a cell if the the cell containt a certain formula.

Please find below an extract of the macro.

Option Explicit

Sub copyPasteValue()

Dim cellContent As String

Dim endRow As Integer
Dim endCol As Integer
Dim r As Integer
Dim c As Integer

Dim activeCellColumn As Integer
Dim activeCellRow As Integer

activeCellColumn = ActiveCell.Column
activeCellRow = ActiveCell.Row


endRow = Cells(Rows.Count, activeCellColumn).End(xlUp).Row
endCol = Cells(activeCellRow, Columns.Count).End(xlToLeft).Column


For c = activeCellColumn To endCol

    For r = activeCellRow To endRow
        
        cellContent = Cells(r, c).Formula
        
        If InStr(1, cellContent, "GetCtData") Then
           Cells(r, c).Copy
           Cells(r, c).PasteSpecial Paste:=xlPasteValues
        End If

    Next

Next

Cells(activeCellRow, activeCellColumn).Select

If ActiveWorkbook.Name = "VALUE.xlsx" Then
    ActiveWorkbook.Save
Else
    ActiveWorkbook.SaveAs Filename:="VALUE"
End If

End Sub

The macro is very slow and can take up to more than 1 hour per sheet for certain work. Does anyone have an idea on how to increase the speed ?

Thank you in advance for your help.

答案1

得分: 1

代替循环所有单元格(这需要时间),我建议使用“查找”功能,这样Excel会查找公式:

Sub replaceFunction()
    Const FunctionName = "GetCTData" 
    With ActiveCell.Cells                  ' 考虑指定工作表。
        Dim hit As Range
        Set hit = .Find(What:=FunctionName, _
                       LookIn:=xlFormulas2, _
                       LookAt:=xlPart)
        If Not hit Is Nothing Then         ' 至少找到一个包含公式的单元格
            Do
                hit.Value = hit.Value      ' 替换值
                Set hit = .FindNext(hit)   ' 查找下一个包含公式的单元格
            Loop While Not hit Is Nothing  ' ...直到找不到为止。
        End If
    End With
End Sub

这看起来有点奇怪,但hit.Value = hit.Value将公式替换为其实际值。

英文:

Instead looping over all cells (which is time consuming), I would suggest to use the Find function so Excel looks for the formula:

Sub replaceFunction()
    Const FunctionName = "GetCTData" 
    With ActiveCell.Cells                  ' Consider to specify the worksheet.
        Dim hit As Range
        Set hit = .Find(What:=FunctionName , _
                       LookIn:=xlFormulas2, _
                       LookAt:=xlPart)
        If Not hit Is Nothing Then         ' At least one cell found with formula
            Do
                hit.Value = hit.Value      ' Replace value
                Set hit = .FindNext(hit)   ' Search next cell with formula
            Loop While Not hit Is Nothing  ' ...until no more found.
        End If
    End With
End Sub

It may look a little bit strange, but hit.Value = hit.Value replaces the formula with it's actual value.

答案2

得分: 0

循环查找"GetCtData"似乎是不可避免的,但你应该避免使用剪贴板

即,不要使用你的两行"Copy"和"PasteSpecial",而要使用:

Cells(r, c).Value = Cells(r, c).Value

这将覆盖单元格的值。

你还可以删除存储公式在变量'cellContent'中的那行代码,所以底部的代码块可以重写如下:

For c = activeCellColumn To endCol
    For r = activeCellRow To endRow

        If InStr(1, Cells(r, c).Formula, "GetCtData") Then
            Cells(r, c).Value = Cells(r, c).Value
        End If

    Next r
Next c

避免使用剪贴板将节省大量时间,而且也更不容易出错(避免使用剪贴板是一个通用的规则,值得一读)。

英文:

Looping to look for "GetCtData" sounds unavoidable but what you should do is avoid the clipboard

I.e. instead of your two lines "...Copy" and "...PasteSpecial", use:

Cells(r, c).Value = Cell(r, c).Value

...which overwrites the cell with its own value

You can also cut out the line that stores the formula in the variable 'cellContent', so the bottom block of code can be re-written as follows:

For c = activeCellColumn To endCol
    For r = activeCellRow To endRow
     
        If InStr(1, Cells(r, c).Formula, "GetCtData") Then
           Cells(r, c).Value = Cells(r, c).Value
        End If

    Next r
Next c

Bypassing the clipboard will save a fair amount of time, and is also less buggy (worth a read on avoiding the clipboard as a general rule)

答案3

得分: 0

以下是代码的翻译部分:

子公式转换为数值()

    常数 FORMULA_IDENTIFIER 作为字符串 = "GetCtData"

    Dim ws As Worksheet: Set ws = ActiveSheet ' 改进!
    Dim rg As Range: Set rg = ws.UsedRange

    Dim rCount As Long: rCount = rg.Rows.Count
    Dim cCount As Long: cCount = rg.Columns.Count

    Dim rOffset As Long: rOffset = rg.Row - 1
    Dim cOffset As Long: cOffset = rg.Column - 1

    Dim Data(): Data = rg.Formula

    Dim drg As Range, c As Long, r As Long, fr As Long, lr As Long, cc As String

    对于 c = 1 到 cCount
        cc = GetColumnString(c + cOffset)
        对于 r = 1 到 rCount
            如果 InStr(Data(r, c), FORMULA_IDENTIFIER) > 0 Then
                如果 fr = 0 则 fr = r + rOffset
                lr = r + rOffset
            否则
                如果 fr > 0 则 CombineAndReset drg, fr, ws, lr, cc
            结束 如果
        下一个 r
        如果 fr > 0 则 CombineAndReset drg, fr, ws, lr, cc
    下一个 c

    如果 drg 为 Nothing        MsgBox "未找到包含 """ & FORMULA_IDENTIFIER _
            & """ 的公式。未执行任何操作。", vbExclamation
        退出子程序
    结束 如果

    粘贴区域数值 drg

    With ws.Parent
        如果 .Name = "VALUE.xlsx" 则 .Save Else .SaveAs 文件名 = "VALUE"
    结束 With

    MsgBox "包含 """ & FORMULA_IDENTIFIER _
        & """ 的公式已替换为数值。", vbInformation

结束 子程序

以下是帮助部分的翻译:

函数 GetColumnString(ByVal ColumnNumber As Long) As String
    Dim Remainder As Long
        Remainder = (ColumnNumber - 1) Mod 26
        GetColumnString = Chr(Remainder + 65) & GetColumnString
        ColumnNumber = Int((ColumnNumber - Remainder) \ 26)
    直到 ColumnNumber = 0
结束 函数

子程序 CombineAndReset( _
        通过引用 urg As Range, _
        通过引用 fr As Long, _
        ByVal ws As Worksheet, _
        ByVal lr As Long, _
        ByVal cc As String)
    合并范围 urg, ws.Range(cc & fr & ":" & cc & lr)
    fr = 0
结束 子程序

子程序 CombineRanges(通过引用 urg As Range, ByVal arg As Range)
    如果 urg 为 NothingSet urg = arg Else Set urg = Union(urg, arg)
结束 子程序

子程序 粘贴区域数值(ByVal rg As Range)
    Dim arg As Range
    对于 每个 arg 在 rg.Areas 中arg.Value = arg.Value: 下一个 arg
结束 子程序
英文:

Formulas To Values

Main

<!-- language: lang-vb -->

Sub FormulasToValues()

    Const FORMULA_IDENTIFIER As String = &quot;GetCtData&quot;

    Dim ws As Worksheet: Set ws = ActiveSheet &#39; improve!
    Dim rg As Range: Set rg = ws.UsedRange
    
    Dim rCount As Long: rCount = rg.Rows.Count
    Dim cCount As Long: cCount = rg.Columns.Count
    
    Dim rOffset As Long: rOffset = rg.Row - 1
    Dim cOffset As Long: cOffset = rg.Column - 1
    
    Dim Data(): Data = rg.Formula
    
    Dim drg As Range, c As Long, r As Long, fr As Long, lr As Long, cc As String
    
    For c = 1 To cCount
        cc = GetColumnString(c + cOffset)
        For r = 1 To rCount
            If InStr(Data(r, c), FORMULA_IDENTIFIER) &gt; 0 Then
                If fr = 0 Then fr = r + rOffset
                lr = r + rOffset
            Else
                If fr &gt; 0 Then CombineAndReset drg, fr, ws, lr, cc
            End If
        Next r
        If fr &gt; 0 Then CombineAndReset drg, fr, ws, lr, cc
    Next c
                
    If drg Is Nothing Then
        MsgBox &quot;No formulas containing &quot;&quot;&quot; &amp; FORMULA_IDENTIFIER _
            &amp; &quot;&quot;&quot; found. No action taken.&quot;, vbExclamation
        Exit Sub
    End If
    
    PasteAreasValues drg

    With ws.Parent
        If .Name = &quot;VALUE.xlsx&quot; Then .Save Else .SaveAs Filename:=&quot;VALUE&quot;
    End With
   
    MsgBox &quot;Formulas containing &quot;&quot;&quot; &amp; FORMULA_IDENTIFIER _
        &amp; &quot;&quot;&quot; replaced with values.&quot;, vbInformation

End Sub

The Help

<!-- language: lang-vb -->

Function GetColumnString(ByVal ColumnNumber As Long) As String
    Dim Remainder As Long
    Do
        Remainder = (ColumnNumber - 1) Mod 26
        GetColumnString = Chr(Remainder + 65) &amp; GetColumnString
        ColumnNumber = Int((ColumnNumber - Remainder) \ 26)
    Loop Until ColumnNumber = 0
End Function

Sub CombineAndReset( _
        ByRef urg As Range, _
        ByRef fr As Long, _
        ByVal ws As Worksheet, _
        ByVal lr As Long, _
        ByVal cc As String)
    CombineRanges urg, ws.Range(cc &amp; fr &amp; &quot;:&quot; &amp; cc &amp; lr)
    fr = 0
End Sub

Sub CombineRanges(ByRef urg As Range, ByVal arg As Range)
    If urg Is Nothing Then Set urg = arg Else Set urg = Union(urg, arg)
End Sub

Sub PasteAreasValues(ByVal rg As Range)
    Dim arg As Range
    For Each arg In rg.Areas: arg.Value = arg.Value: Next arg
End Sub

huangapple
  • 本文由 发表于 2023年2月27日 17:43:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/75578844.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定