".text", ".value"和".value2"有什么区别?它们在IT技术中的应用是什么?

235

.text.value.value2 有什么区别?比如说,在什么情况下应该使用 target.text、target.value 或 target.value2?


如果目标值(Target.Value)不能强制转换为字符串,则 LCase(Target.Value) 将失败,因为 LCase 需要一个字符串作为参数。您应该首先检查 VarType,如我的答案所示。还要注意,您可以使用 UCase 替代,并直接将其与“HLO”进行比较:在文字上操作没有太大意义。 - Bathsheba
感谢关于VarType的信息。至于使用LCase或UCase,我用哪个都无所谓。有些人将其输入为hlo,而其他人将其输入为HLO。从我所看到的来看,小写的更常用一些。 - Chris
7个回答

305

.Text提供了一个代表单元格屏幕上显示的字符串。使用.Text通常是不明智的,因为你可能会得到####

.Value2给出了单元格的实际值(包括空值、字符串、错误、数字(双精度)或布尔值)。

.Value.Value2相同,除非单元格被格式化为货币或日期,这将给你VBA货币(可能截断小数位)或VBA日期。

使用.Value.Text通常是不明智的,因为你可能无法获得单元格的真实值,而且它们比.Value2更慢。

有关更详细的讨论,请参见 Text vs Value vs Value2


6
我会使用“Format”函数来控制将数字转换为字符串的格式:var = Format(Range("a1").Value2, "#")。 - Charles Williams
2
我希望这不是一个单独的问题,但是:默认值是什么?OP含糊地声称省略文本/值/值2是有问题的,但肯定会默认为其中的一个,对吧? - Martin F
1
默认值为.Value。 - Charles Williams
3
问题在于Excel没有真正的日期数据类型-Excel日期和时间只是依赖于用户应用或更改的格式而呈现为日期、时间、货币或数字的双精度浮点数。因此,Value将Excel双精度浮点数强制转换为VBA日期,但Value2不进行任何强制转换... 对于日期来说,将双精度浮点数强制转换为日期可能不会造成任何损害,只要代码理解其依赖于可更改的格式即可:两种方法都有优缺点-我们真正需要的是更多的本地Excel数据类型以避免这个问题。 - Charles Williams
2
当我想将一个单元格的值设置为另一个单元格的值时,不进行类型转换(例如,不将存储为文本的数字转换为数字),我使用以下代码:Format $(Range.Value2,Range.NumberFormat) - ChrisB
显示剩余6条评论

62

6
@Chris,始终将.Value用作文本和数字的标准属性。在考虑日期和某些数字时,请使用.Value2。如果您需要保留单元格/范围中任何内容的格式,请始终使用.Text。因此,您的问题示例是正确的! - Kazimierz Jawor
2
为什么日期从10:12变成了10:05?是打错了吗? - Katrin
4
我认为这只是生成结果和截屏时间之间经过的时间。 - Kazimierz Jawor

26

target.Value将会返回一个Variant类型。

target.Value2也会返回一个Variant类型,但是如果Date被强制转换为Double类型。

target.Text试图强制转换为String类型,如果底层的Variant不能强制转换为String类型,则会失败。

最安全的做法是像下面这样:

Dim v As Variant
v = target.Value 'but if you don't want to handle date types use Value2

在进行显式强制转换之前,使用VBA.VarType(v)检查变量的类型。


11

关于C#中的约定。假设您正在读取一个包含日期的单元格,例如2014-10-22。

使用:

.Text,您将获得格式化后的日期表示形式,就像在屏幕上查看的一样:
2014-10-22。此属性的类型始终为string,但可能不总是返回令人满意的结果。

.Value,编译器尝试将日期转换为DateTime对象:{2014-10-22 00:00:00}。在读取日期时可能非常有用。

.Value2,给出单元格的实际基础值。对于日期,它是一个日期序列:41934。该属性的类型可以根据单元格内容而异。然而,对于日期序列,类型为double

因此,您可以在dynamicvarobject中检索和存储单元格的值,但请注意,该值将始终具有某种固有类型,您需要对其进行操作。

dynamic x = ws.get_Range("A1").Value2;
object  y = ws.get_Range("A1").Value2;
var     z = ws.get_Range("A1").Value2;
double  d = ws.get_Range("A1").Value2;      // Value of a serial is always a double

4

.Text是格式化单元格的显示值;.Value是可能附带日期或货币指示符的单元格的值;.Value2是剥离了任何多余信息的原始基础值。

range("A1") = Date
range("A1").numberformat = "yyyy-mm-dd"
debug.print range("A1").text
debug.print range("A1").value
debug.print range("A1").value2

'results from Immediate window
2018-06-14
6/14/2018 
43265 

range("A1") = "abc"
range("A1").numberformat = "_(_(_(@"
debug.print range("A1").text
debug.print range("A1").value
debug.print range("A1").value2

'results from Immediate window
   abc
abc
abc

range("A1") = 12
range("A1").numberformat = "0 \m\m"
debug.print range("A1").text
debug.print range("A1").value
debug.print range("A1").value2

'results from Immediate window
12 mm
12
12

如果您正在处理单元格的值,则读取原始的.Value2比.Value或.Text略快。 如果您正在定位错误,则.Text将返回像#N/A这样的文本,可以与字符串进行比较,而.Value和.Value2将无法将其返回值与字符串进行比较。 如果您对数据应用了一些自定义单元格格式,则在构建报告时使用.Text可能是更好的选择。


2

Value2 几乎总是在从 VBA 读取或写入 Excel 单元格或范围时的最佳选择。

Range.Value2 '<------Best way

以下每个都可以用来从范围内进行读取:

v = [a1]
v = [a1].Value
v = [a1].Value2
v = [a1].Text 
v = [a1].Formula
v = [a1].FormulaR1C1

以下每个都可以用于写入范围:
[a1] = v
[a1].Value = v
[a1].Value2 = v
[a1].Formula = v
[a1].FormulaR1C1 = v

如果需要从大范围读取多个值,或者写入多个值,整体操作一次比逐个单元格操作快上几个数量级:

arr = [a1:z999].Value2

如果arr是Variant类型的变量,上述行实际上创建了一个26列宽和999行高的变体OLE SAFEARRAY结构,并将Variant arr指向内存中的SAFEARRAY结构。请注意保留HTML标签。
[a1].Resize(UBound(arr), UBound(arr, 2).Value2 = arr

以上代码将整个数组一次性写入工作表中,无论数组有多大(只要它能适应工作表)。

范围对象的默认属性是Value属性。因此,如果未为范围指定属性,则默认情况下会静默引用Value属性。

但是,Value2是访问范围值最快的属性,在读取时返回真实的底层单元格值。它忽略数字格式、日期、时间和货币,并将数字作为VBA双精度数据类型始终返回。由于Value2试图做更少的工作,所以执行速度比Value稍微快一些。

另一方面,Value属性检查单元格值是否具有日期或时间的数字格式,并在这些情况下返回VBA日期数据类型的值。如果您的VBA代码将使用日期数据类型,那么使用Value属性检索它们可能是有意义的。将VBA日期数据类型写入单元格将自动使用相应的日期或时间数字格式格式化单元格。将VBA货币数据类型写入单元格也将自动将货币数字格式应用于相应的单元格。

类似地,Value检查单元格货币格式,然后返回VBA货币数据类型的值。这可能会导致精度损失,因为VBA货币数据类型仅识别四个小数位(因为VBA货币数据类型实际上只是一个64位整数,乘以10000)。因此,最多将值舍入到四个位置。而且奇怪的是,使用Value将VBA货币变量写入工作表范围时,该精度仅缩减到两个小数位。

只读的Text属性始终返回VBA字符串数据类型。由Range.Text返回的值是每个单元格中显示的文本表示形式,包括数字格式、日期、时间、货币和错误文本。这不是将数字值传递到VBA的有效方法,需要隐式或显式强制转换。当列太窄时,Text将返回#######,当调整某些行高度时,它将变得更慢。与ValueValue2相比,Text始终非常慢。但是,由于Text保留了单元格值的格式外观,因此Text可能很有用,特别是用于使用正确格式化的文本值填充用户窗体控件。

同样地,FormulaFormulaR1C1 始终会返回 VBA 字符串数据类型的值。如果单元格包含公式,则 Formula 返回其 A1 样式表示法,FormulaR1C1 返回其 R1C1 表示法。如果单元格具有硬编码值而非公式,则 FormulaFormulaR1C1 忽略所有格式并返回真实的底层单元格值,就像 Value2 一样... 然后进一步将该值转换为字符串。同样地,这不是将数字值导入 VBA 的有效方式,因为需要隐式或显式强制转换。但是,必须使用 FormulaFormulaR1C1 来读取单元格公式。它们应该用于向单元格写入公式。
如果单元格 A1 包含数值 100.25,并带有货币数字格式 $ #,##0.00_);($ #,##0.00),请考虑以下内容:
MsgBox [a1].Value                   'Displays:  100.25
MsgBox TypeName([a1].Value)         'Displays: Currency
 
MsgBox [a1].Value2                  'Displays:  100.25
MsgBox TypeName([a1].Value2)        'Displays: Double
 
MsgBox [a1].Text                    'Displays: $ 100.25
MsgBox TypeName([a1].Text)          'Displays: String
 
MsgBox [a1].Formula                 'Displays: 100.25
MsgBox TypeName([a1].Formula)       'Displays: String
 
MsgBox [a1].FormulaR1C1             'Displays: 100.25
MsgBox TypeName([a1].FormulaR1C1)   'Displays: String
  

1

出于好奇,我想看看 Value 在与 Value2 相比时的表现。经过大约12次类似过程的试验后,我没有看到任何明显的速度差异,因此我始终建议使用 Value。我使用下面的代码对各种范围进行了一些测试。

如果有人发现性能方面有任何不同的情况,请发布。

Sub Trial_RUN()
    For t = 0 To 5
        TestValueMethod (True)
        TestValueMethod (False)
    Next t

End Sub




Sub TestValueMethod(useValue2 As Boolean)
Dim beginTime As Date, aCell As Range, rngAddress As String, ResultsColumn As Long
ResultsColumn = 5

'have some values in your RngAddress. in my case i put =Rand() in the cells, and then set to values
rngAddress = "A2:A399999" 'I changed this around on my sets.



With ThisWorkbook.Sheets(1)
.Range(rngAddress).Offset(0, 1).ClearContents


beginTime = Now

For Each aCell In .Range(rngAddress).Cells
    If useValue2 Then
        aCell.Offset(0, 1).Value2 = aCell.Value2 + aCell.Offset(-1, 1).Value2
    Else
        aCell.Offset(0, 1).Value = aCell.Value + aCell.Offset(-1, 1).Value
    End If

Next aCell

Dim Answer As String
 If useValue2 Then Answer = " using Value2"

.Cells(Rows.Count, ResultsColumn).End(xlUp).Offset(1, 0) = DateDiff("S", beginTime, Now) & _
            " seconds. For " & .Range(rngAddress).Cells.Count & " cells, at " & Now & Answer


End With


End Sub

enter image description here


你的时间完全被引用单元格的开销淹没了(逐个单元格遍历对性能来说是一场灾难)。尝试使用大范围的单元格分配给变量,并创建一个二维变量数组。 - Charles Williams
@CharlesWilliams 是的,你说得对。我称之为尴尬代码,意思是在我不知道其他东西(例如数组的重要性)之前编写的代码,回顾过去,我...你猜对了....感到尴尬。无论如何,谢谢你的提示。我可能会稍后重新发布一些内容。 - pgSystemTester
当源数据包含日期和时间时,差异将更加明显。.Value2 更快。 - Excel Hero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接