定义大型VBA字符串的最佳方法 - 即heredoc等效方法是什么?

17

我该如何定义VBA中的大字符串?除了编写类似以下代码的方式,还有更好的方法吗?

Dim largeString as String
largeString = "This is a long block of text that I want to fill " & _
              "into a form field. I need to make sure I pay attention " & _
              "to spacing and carriage return issues while doing so. " & _
              "I also have to use quotes liberally, the concatenation " & _
              "operator, and the continuance underscore to make sure " & _
              "VBA can parse my code." & vbCr & vbCr & _
              "It's kind of a pain in the ass and I wish I could use " & _
              "a heredoc instead, letting me copy and paste the block" & _
              "of text I need from another source and shove it into " & _
              "a string."

编辑: 哎呀,还有25行的限制吗?那么好的缩进和80个字符宽度都白费了,这只给我足够的空间写几段像样的段落。


或者在程序中动态创建字符串 Dim s as String, 然后赋值为 s = String(1337, "a") - user2140173
我认为你会看到我的答案解决了这个问题。https://dev59.com/xHA65IYBdhLWcg3wuRB8#67154039 - johny why
5个回答

14

我更喜欢以这种方式来做:

Dim lStr As String
lStr = ""

lStr = lStr & "This is a long block of text that I want to fill "
lStr = lStr & "into a form field. I need to make sure I pay attention "
lStr = lStr & "to spacing and carriage return issues while doing so. "
lStr = lStr & "I also have to use quotes liberally, the concatenation "
lStr = lStr & "operator, and the continuance underscore to make sure "
lStr = lStr & "VBA can parse my code." & vbCr & vbCr
lStr = lStr & "It's kind of a pain in the ass and I wish I could use "
lStr = lStr & "a heredoc instead, letting me copy and paste the block"
lStr = lStr & "of text I need from another source and shove it into "
lStr = lStr & "a string."

我认为这种方法比行继续方法更易于使用,并且没有行号限制。您可以注释掉各个行,这对于调试SQL字符串非常有用。

处理长字符串时,我发现使用短变量名更容易,因为VBA没有类似于+=运算符的等价物。largeString = largeString & ""占用太多空间并且重复,所以缩短字符串名称会使格式有些可接受。

对于非常大的文本块,请在文本编辑器中编写,然后将其复制并粘贴到过程中。然后复制。

lStr = lStr & "

复制并粘贴到每行开头。VBA编辑器将自动在行末添加引号,使该过程变得简单。


2
我喜欢这种语法来实现行的续行,但我问:“为什么”?为什么有人需要如此大的字符串,不能将其放在其他地方?这让我想起那些认为编程的最高境界是拼接的VBA流氓们。 - Chris Hayes
1
可能因为要求将查询保留在Excel文件内部,而一些人更喜欢将其放在文件中,而不是作为单元格值? - pablete
1
这里的限制是64k - 这就是单个VBA代码模块可以达到的最大大小。 - Blackhawk
@ChrisHayes 假设我需要将一个大的XML文件传递给另一个应用程序,这似乎是最简单和最干净的解决方案。 - Chinmay Kamat
1
现代处理器可能不会有太大问题,但在ASP中重复连接字符串比在单个多行赋值(或多个response.write)中生成长字符串要消耗更多的处理器资源。这个页面有一些速度比较 - http://www.somacon.com/p236.php - 大约可以提高23倍的性能! - Stephen R
显示剩余2条评论

13

不,这已经是最好的了。

对于非常长的字符串,可以将它们保存在单独的文件中,或使用一些应用程序功能。例如,在Word中,您可能希望将字符串存储在文档变量、隐藏文本或自动图文集中。在Excel中,您可以考虑使用隐藏工作表来存储长字符串常量。


是的,我正在考虑那样做。另一个选择是制作一个永远不会显示的表单,其中包含包含所需文本的文本框,然后只需使用它们的.Value()。唯一的缺点是,我可以看到这种解决方法的维护可能是一场巨大的噩梦。不过,VBA的维护也并非易如反掌:/ - Oesor
我喜欢这个答案,但是或许可以像文本流一样将其写入文本文件中。不过也许这有点过了? - Chris Hayes
@Chris:我不确定你的意思。实际上,将字符串保存在文本文件中是我提到的第一个选项。 - Dirk Vollmar
1
使用文本文件而不是嵌入在Office文档中的代码的缺点是现在你需要维护多个文件。对于一个小型、自包含的VBA应用程序来说,这有点过头了。 - Oesor

3

另一种方法是将文本存储在注释中,然后在函数中解析它。不需要外部文件,易于阅读。

' TEXT to retrieve:
'   SELECT
'   field1, field2
'   FROM table1

Function SQL_in_comments()
    SQL_in_comments = Replace(Replace(Application.VBE.ActiveCodePane.CodeModule.Lines(2, 3), "'   ", ""), "'", "")
End Function

1
我发现这种方法非常方便,可以嵌入/定义大型VBA字符串,将整个文本文件嵌入到字符串变量中进行读取,或者重新创建嵌入的二进制文件,所有这些都可以通过模块顶部的注释来完成。
首先,使用以下VBS脚本从文件(文本或二进制)创建压缩的、Base64编码的、每行255个字符宽的注释行:
Dim wsh, fso
Set wsh = wscript.CreateObject("wscript.Shell")
Set fso = CreateObject("Scripting.fileSystemObject")

On Error Resume Next

'DROP-IN HANDLER
'***************
If WScript.Arguments.Count > 0 Then
    For each arg in WScript.Arguments  
        path_and_filename = path_and_filename & arg
    Next
    tokens = Split(path_and_filename, "\")
    infilename = tokens(UBound(tokens))
    For i=0 To UBound(tokens)-1
        path = path & tokens(i) & "\"
    Next
else
    wsh.Popup "No drop-in file!" & vbcrlf & vbcrlf & "Drag the icon of the file you want to compress and convert to VBA comment format" & vbcrlf & "and drop it into the Bin2VBAComment.vbs icon.", 0, "Error!", 4096+16
    wscript.quit
End If

outfilename = infilename & "-Comment.txt"
tempfile = fso.GetTempName

'MAKECAB NEEDS THE TRAILING "\" REMOVED FROM THE PATH TO COMPRESS THE FILE
'*************************************************************************
wsh.run ("cmd /c makecab  /L """ & left(path,len(path)-1) & """ """ & WScript.Arguments(0) & """ " & tempfile ),0,True

'BASE64 ENCODE THE COMPRESSED CAB FILE
'*************************************
bytes_ = readBytes(path & tempfile)
base64_ = encodeBase64(bytes_)

'REMOVE THE LINE FEEDS AT CHARACTER 72...
'****************************************
base64_ = replace(base64_,vblf,"")

'...SO WE CAN PUT THEM AFTER CHARACTER 255
'*****************************************
x=1
linecount_=0
do while x<len(base64_)
    out_ = out_ & "'" & Mid(base64_,x,255) & vbcrlf
    x=x+255
    linecount_=linecount_+1
loop

'CREATE THE TEXT FILE
'********************
set objOutputFile = fso.CreateTextFile(path & outfilename, TRUE)
objOutputFile.WriteLine("'" & infilename)
objOutputFile.WriteLine("'" & linecount_)
objOutputFile.Write(out_)
objOutputFile.Close

'TIDY-UP
'*******
if fso.FileExists(path & tempfile) then
    Set aFile = fso.GetFile(path & tempfile)
    aFile.Delete
end if

wsh.Popup "Done!", 2, "Bin2VBAComment.vbs", 4096+48

private function readBytes(file)
    dim inStream
    set inStream = WScript.CreateObject("ADODB.Stream")
    inStream.Open
    inStream.type= 1 'TypeBinary
    inStream.LoadFromFile(file)
    readBytes = inStream.Read()
end function

private function encodeBase64(bytes)
    dim DM, EL
    Set DM = CreateObject("Microsoft.XMLDOM")
    Set EL = DM.createElement("tmp")
    EL.DataType = "bin.base64"
    EL.NodeTypedValue = bytes
    encodeBase64 = EL.Text
end function

作为一个例子,我插入了一个二维码图片,创建了"QR.png-Comment.txt"文件。
'QR.png
'3
'TVNDRgAAAAA0AgAAAAAAACwAAAAAAAAAAwEBAAEAAAAAAAAAQwAAAAEAAQDoAQAAAAAAAAAAGVdVjCAAUVIucG5nAJuWuhbpAegBQ0vrDPBz5+WS4mJgYOD19HAJYmBgnADCjMxAkV2FS/qBFGNxkLsTw7pzMi+BHJZ0R19HBoaN/dx/ElmBfLYAnxBXIP3///+lN+ffBbI4CzwiixkY+A6DMOPx/BUpQCOyPV0cQypuvb0Z2HVEgachUZH99VX
'biINH3ZJjyreF/jb60J6Ykb2vkutkhvMsPg4WpkYHBYET2ydJZJ0o1X760FDHUaF7yZ048fRzZwpjS7wFTrB1hj90LVwlFN3kEKeoG9ymyvHENchdi+WT2EdxVXWbeScETuhymi4x2futEKja6GPgrvsvL9c4KGgfqZq79Om+5yEiHCndL5x1Tyw4WaUrf1ybxch1C8dWvv6opSu3HQzaCLTg48WtBy/IPnRSsD7y/MiF+RURxi0OZR0l7Zm/sh
'w8/8txMUUq/it4LHHP9mcjd+OaiWa2568xTK7nN+0L50hpi/C99U7B6/HBfyccFVYfaVe97fH3izgfU6VfTr9ATO2/WTsFTohNDJu8MTxs2uOKVodlHiWmlTtsm47NT+UEmiXz9Y6zOfctoeAmh3uK0zLf6nn9bnOY5pm+VkHeL4XjybQrhlJZkc/kX/Zf/3c70kGhPEVNQQASZvFuQb8YuQNVPbev2/YAGBcMnq5+LuucEpoA

将此文本粘贴到模块的顶部(例如Module1)。
在"ThisWorkbook"宏的顶部,创建一个全局变量用于文件名,以便在Workbook_BeforeClose时进行删除。
Public BinFilename As String

然后添加以下宏:

Private Sub Workbook_Open()

Set wsh = CreateObject("wscript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")

'ACTIVATE THE MODULE CONTAINING THE COMPRESSED TEXT AT THE TOP
ThisWorkbook.VBProject.VBComponents("Module1").Activate

'GET THE FILE NAME FROM THE FIRST LINE
filename_ = Replace(Application.VBE.ActiveCodePane.CodeModule.Lines(1, 1), "'", "")

'SET THE GLOBAL VARIABLE SO WE CAN DELETE THE FILE ON CLOSE
BinFilename = filename_

'GET THE NUMBER OF LINES FROM THE SECOND LINE
no_of_lines_ = Replace(Application.VBE.ActiveCodePane.CodeModule.Lines(2, 1), "'", "")

'GET THE BASE64 TEXT FROM LINE 3 ONWARDS, REMOVING COMMENT TICKS, CARRIAGE RETURNS AND SPACES
base64_ = Replace(Application.VBE.ActiveCodePane.CodeModule.Lines(3, no_of_lines_), "'", "")
base64_ = Replace(base64_, vbCrLf, "")
base64_ = Replace(base64_, " ", "")

'GET THE LOCAL TEMP DIRECTORY AND A TEMP FILE NAME
tempFolderPath = fso.GetSpecialFolder(2)
tempfilename = tempFolderPath & "\" & fso.GetTempName

'CONVERT THE BASE64 TEXT TO POPULATE THE TEMP COMPRESSED FILE
    Dim DM, EL
    Set DM = CreateObject("Microsoft.XMLDOM")
    Set EL = DM.createElement("tmp")
    EL.DataType = "bin.base64"
    EL.Text = base64_

    Dim binaryStream
    Set binaryStream = CreateObject("ADODB.Stream")
    binaryStream.Type = 1 'adTypeBinary
    binaryStream.Open
    binaryStream.Write EL.NodeTypedValue
    binaryStream.SaveToFile tempfilename, 2

'DECOMPRESS THE TEMP FILE TO RECREATE THE ORIGINAL FILE
wsh.Run ("cmd /c expand -F:* """ & tempfilename & """ """ & tempFolderPath & "\" & filename_ & """"), 0, True

'DELETE THE TEMP FILE
fso.DeleteFile (tempfilename)

'DECIDE WHAT TO DO WITH THE FILE
If LCase(Right(filename_, 3)) = "txt" Then
    'LOAD THE TEXT FILE CONTENTS INTO A VARIABLE
    LoadFileStr = fso.OpenTextFile(tempFolderPath & "\" & filename_, 1).readall
    MsgBox LoadFileStr
Else
    'ASSUME IT'S A BINARY FILE AND RUN IT
    wsh.Run ("cmd /c " & Chr(34) & tempFolderPath & "\" & filename_ & Chr(34)), 0, False
End If


End Sub

并且

Private Sub Workbook_BeforeClose(Cancel As Boolean)

'DELETE THE CREATED FILE ON EXIT
    Set fso = CreateObject("Scripting.FileSystemObject")
    fso.DeleteFile (fso.GetSpecialFolder(2) & "\" & BinFilename)

End Sub

Workbook_Open宏查看Module1注释,从第一行提取原始文件名并将其传递给"BinFilename"以进行删除。然后从第2行获取行数,并从第3行开始获取Base64文本。
Base64文本被解码,使用Microsoft Expand函数进行解压缩,然后在临时目录中创建文件。
读取一个.txt文件,并将其内容传递给"LoadFileStr"变量。任何其他文件类型都将作为二进制文件运行。
Workbook_BeforeClose在关闭时从临时目录中删除该文件。

0
在Excel中,有一种比其他答案更简单的方法。让Excel为您完成繁重的工作。这种方法不需要将字符串分成较小的块 - 它可以一次处理多达32,767个字符。
将字符串放入一个单元格中。为此目的设置一个临时工作表。给该单元格命名,例如MyLongString[MyLongString] = "长期以来,人们一直认为当读者查看页面布局时,可读内容会分散他们的注意力。使用Lorem Ipsum的要点是它具有相对正常的字母分布,而不是使用'Content here, content here',使其看起来像可读的英语。许多桌面出版软件和网页编辑器现在使用Lorem Ipsum作为默认的模型文本,并且搜索“lorem ipsum”将发现仍处于初级阶段的许多网站。多年来,各种版本已经发展起来,有时是偶然的,有时是有意的(注入幽默等)。 现在您可以在VBA中引用该名称,如[MyLongString]。例如: MsgBox [MyLongString]

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