如何在使用Visual Basic的Print语句时避免额外的"回车符"?

11

在使用Visual Basic时,我对Print语句的行为感到困惑,有时候以下语句:会导致在行末添加额外的回车符"^M",但有时候却不会。我想知道这是为什么?

filePath = "d:\tmp\FAE-IMM-Report-2012-Week.org"
    If Dir(filePath) <> "" Then
        Kill filePath
    End If
    outFile = FreeFile()
    Open filePath For Output As outFile
    Print #outFile, "#+TITLE:     Weekly Report"

会产生

#+TITLE:     Weekly Report^M

当我希望没有 ^M 的时候:

#+TITLE:     Weekly Report

在一个测试程序中,几乎相同的代码不会产生"^M"。

请帮忙!非常感谢。

经过进一步实验,我发现以下建议使用vbNewline和";"在打印内容末尾,仍然不能解决我的问题。

经过仔细分离,我发现问题的原因是一个看起来像空格但并不完全相同的字符,跟随换行和回车。在打印包含有冒犯字符串的文本之前,没有回车符,但是一旦该行被打印,那么每一行(包括之前打印的行)都将有回车符。

由于我对VBA的技能还不太好,我不确定确切的冒犯字符串是什么。

这里是从电子表格单元格复制的冒犯文本:

   "There is something invisible after this visible text 
After the invisible text, then there might be a carriage return $Chr(13) and/or newline"

我不确定将内容粘贴到Web浏览器是否会保留该内容。通过将其粘贴到Emacs中,我没有看到回车符号,而如果存在回车符号,Emacs应该会显示它。因此,我猜测该字符串中没有回车符号。

以下是演示问题的程序:

    Sub DemoCarriageReturnWillAppear()
    Dim filePath As String
    Dim outFile
    Dim offendingText
    filePath = "d:\tmp\demoCarriageReturn.org"
    If Dir(filePath) <> "" Then
        Kill filePath
    End If
    outFile = FreeFile()
    Open filePath For Output As outFile
    Print #outFile, "#+AUTHOR:    Yu Shen" & vbNewLine;
    Close #outFile 'At this moment, there is no carriage return
    Open filePath For Append As outFile
    offendingText = ThisWorkbook.Worksheets("Sheet1").Range("A1")
    Print #outFile, offendingText & vbNewLine;
    Close #outFile 'Now, every line end has carriage return.
    'It must be caused by something offending at the above print out content.
End Sub

以下是上述过程的最终结果:

    #+AUTHOR:    Yu Shen^M

There is something invisible after this visible text 
After the invisible text, then there might be a carriage return $Chr(13) or newline^M

请注意,上面的 "^M" 是由我添加的,因为在浏览器中回车符不可见。

如果您感兴趣,我可以发送带有有问题内容的 Excel 文件。

我需要您的帮助,以避免那些有问题的字符串或回车符。(我甚至尝试使用字符串替换回车符或换行符,因为我发现一旦手动删除造成另一行改变的内容,问题就会消失。但是调用 Replace 来替换 vbNewline、Chr$(13) 或 vbCrLf 没有任何效果。

感谢您的进一步帮助!

Yu


1
程序中打印行的不同之处是什么,它不会打印^M? - poke
2个回答

24

使用分号来避免换行:

Print #outFile, "#+TITLE:     Weekly Report";
                                            ^
                                            ^

如果您在语句中犯错,VB编辑器通常会添加分号,这可能解释了为什么有时输出新行,有时不输出。

新诊断例程

我们需要知道单元格A1中导致问题的字符。

将以下子例程放置在您的模块之一中。

Public Sub DsplInHex(Stg As String)

  Dim Pos As Long

  For Pos = 1 To Len(Stg)
    Debug.Print Hex(AscW(Mid(Stg, Pos, 1))) & " ";
  Next
  Debug.Print

End Sub

前往VB编辑器的立即窗口,输入以下文本,然后输入Return:

DsplInHex(Sheets("Sheet1").range("A1"))
在这一行下面,你应该会看到类似于54 65 73 74 31的东西。这是单元格中每个字符的代码值列表。我预计我们会在列表末尾看到A——换行符的代码或D——回车符的代码。
将光标定位在单元格A1中。点击F2进行编辑选择,然后点击退格键删除不可见的尾随字符,最后点击回车键结束编辑。返回即时窗口,将光标定位到DsplInHex(Sheets("Sheet1")。range("A1"))的末尾,然后点击返回。尾随字符应该已经消失了。
尝试一下并汇报结果。祝你好运。

1
谢谢!你的想法可行。实际上,加上分号之后就不会有任何换行符了。连续的打印语句会在同一行上。因此,在一行打印结束时,应该像以下这样:Print#outFile,"#+TITLE: Weekly Report"&vbNewLine; 以达到期望的结果并在结尾处有换行符。 - Yu Shen
抱歉,我没有像应该的那样清楚地解释分号的目的。如果您阅读“Print #语句”的文档,您会在“charpos”下找到分号的提及。"XX"&vbNewLine;"XX"完全相同。vbNewLine添加了一个额外的新行,因此您可以在单个语句中输出多行。;表示下一个输出将立即跟随。对于写入即时窗口的Debug.Print,规则是相同的。我建议您进行实验。 - Tony Dallimore
抱歉,这个解决方案并不能解决我的问题。请查看我上面进一步描述的问题。 - Yu Shen
我已经在我的回答中添加了一个新部分,希望这能让我们找出问题所在。 - Tony Dallimore
1
嗨,Tony,你的建议很有用!使用你的脚本,我打印出了冒犯字符串的值。十六进制代码是20 A(即空格后跟换行符)。通过这个发现,我使用替换函数将换行符替换为空格,然后问题就解决了!这是VBA 6(与Excel 2007一起使用)的一个讨厌的bug。 - Yu Shen

4
为了帮助未来的其他人,这里是我的问题和解决方案的摘要。即使在打印语句末尾有分号,每行上的额外回车符实际上是由于一个带有换行符(Chr $(A))的空格字符串引起的,一旦打印此类字符串,则所有先前和随后打印的内容都将有一个额外的回车符!
看起来这是VBA 6(与Excel 2007一起)的一个bug,非常讨厌!
我的解决方法是用空格替换换行符。
感谢Tony的重复帮助,使我终于确定了原因。
以下是演示问题的代码:
Sub DemoCarriageReturnWillAppearOnAllLines()
    Dim filePath As String
    Dim outFile
    Dim offendingText
    filePath = "d:\tmp\demoCarriageReturn.org"
    If Dir(filePath) <> "" Then
        Kill filePath
    End If
    outFile = FreeFile()
    Open filePath For Output As outFile
    Print #outFile, "#+AUTHOR:    Yu Shen" & vbNewLine;
    Close #outFile 'At this moment, there is no carriage return
    Open filePath For Append As outFile
    offendingText = " " & Chr$(10)
    Print #outFile, offendingText & vbNewLine;
    Close #outFile 'Now, every line end has carriage return.
    'It must be caused by the offending at the above print out content.
End Sub

在第一次“Close #outFile”之后,文件demoCarriageReturn.org的内容如下:
#+AUTHOR:    Yu Shen

注意:如果编辑器可以显示回车符号^M,那么就不会出现回车符号。然而,在第二个“Close #outFile”之后,同一文件的内容如下所示,有额外的内容:
#+AUTHOR:    Yu Shen^M

^M 

注意:出现了两个换行符。它们并不是有意添加的。特别地,对于第一行,打印语句已经被执行,在以前的关闭语句处,它被发现没有回车。(为了说明回车符,我必须在这里键入^M。但它在打印输出文件中。)
这就是为什么我认为这是一个错误,因为回车符并不是有意添加的。这是一个不期而遇的问题。
下面的代码表明,如果过滤掉换行字符,则问题将消失。
Sub DemoCarriageReturnWillNotAppearAtAll()
    Dim filePath As String
    Dim outFile
    Dim offendingText
    filePath = "d:\tmp\demoCarriageReturn.org"
    If Dir(filePath) <> "" Then
        Kill filePath
    End If
    outFile = FreeFile()
    Open filePath For Output As outFile
    Print #outFile, "#+AUTHOR:    Yu Shen" & vbNewLine;
    Close #outFile 'At this moment, there is no carriage return
    Open filePath For Append As outFile
    offendingText = " " & Chr$(10)
    Print #outFile, Replace(offendingText, Chr$(10), "") & vbNewLine;
    Close #outFile 'Now, no more carriage return.
    'The only change is removing the linefeed character in the second print statement
End Sub

执行完上述程序后,确实没有回车符!
#+AUTHOR:    Yu Shen

这表明空格和换行符的字符串组合引起了错误,去掉换行符可以避免错误。

下面的代码进一步说明,如果没有冒犯的字符串,即使在print语句的末尾没有换行符和分号,也不会产生不想要的回车符!

Sub DemoCarriageReturnWillNotAppearAtAllEvenWithoutNewLineFollowedBySemiColon()
    Dim filePath As String
    Dim outFile
    Dim offendingText
    filePath = "d:\tmp\demoCarriageReturn.org"
    If Dir(filePath) <> "" Then
        Kill filePath
    End If
    outFile = FreeFile()
    Open filePath For Output As outFile
    Print #outFile, "#+AUTHOR:    Yu Shen"
    Close #outFile 'At this moment, there is no carriage return
    Open filePath For Append As outFile
    offendingText = " " & Chr$(10)
    Print #outFile, Replace(offendingText, Chr$(10), "")
    Close #outFile 'Now, no more carriage return.
    'The real change is removing the linefeed character in the second print statement
End Sub

同时在输出结果中:

#+AUTHOR:    Yu Shen

仍然没有令人讨厌的回车!

这表明,在print语句末尾使用换行符后跟分号并不是解决每行都有回车的问题的方法!真正的解决方案是避免在打印输出内容中出现任何由空格后跟换行符组成的字符串。

Yu


我很高兴我们找到了你问题的源头,但这不是一个错误。你可以使用 Alt+Return 在单元格中输入换行符。人们使用它将长文本字段分成段落。你不需要使用 Replace。如果你编辑了单元格,你会发现光标在文本下面。Backspace 将删除换行符并使光标移动到上一行的末尾。 - Tony Dallimore
但是我很惊讶,仅仅一个空格和换行符的字符串,VBA就会在打印出所有行之前添加回车符!这并不是一个理想的行为。保留带有换行符的打印输出字符串的换行符是可以的。 - Yu Shen
1
Print#语句的规范非常清晰:“如果省略_charpos_,下一个字符将在下一行打印。”也就是说,除非你明确说明不需要添加换行符,否则输出字符串将会加入换行符。Print#fn“Line 1”&vbLf&“Line 2”&vbLf&“Line 3”是有效的。也就是说,一个Print#语句可以产生多行输出。编译器无法知道您是否希望在单元格值的末尾有换行符;它执行的是被告知要做什么而不是您本意要告诉它做什么 - Tony Dallimore
能够在字符串中包含换行符有时很方便。虽然这次对你来说不方便,但这并不意味着它是一个错误。现在你知道可以在打印字符串中包含换行符,很快你就会利用这个功能。 - Tony Dallimore
抱歉,Tony,我只是想表明真正的解决方案是避免在打印内容中使用空格和换行符。你确实帮助我找到了解决方案。但在打印语句末尾使用换行符和分号实际上并不是解决方案。如果没有冒犯性字符串,没有换行符和分号仍然可以,不会引入回车符。有冒犯性字符串时,你的建议仍然没有帮助。非常感谢你的帮助。你帮助我调试了这个问题。 - Yu Shen
这根本不是一个错误。这是Print#语句的完全计划行为。对于每个print#指令,它都会添加一个回车符以在下一行开始新的打印。Tony的建议是正确的。行末的分号将导致Print#语句不添加CRLF。 - Hadi

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