从(非常大的)字符串中快速删除不必要的空格

Quickly remove unnecessary whitespace from a (very large) string

我在 VBA 中处理非常大的(超过 45,000,000 个字符)字符串,我需要删除多余的 whitespace.

一个 space(又名 ASCII 代码 32)是可以的,但任何包含两个或多个连续 space 的部分都应该减少到一个。

我发现了一个类似的问题 ,尽管 OP 对 "very long string" 的定义是 只有 39,000 个字符。接受的答案是使用 Replace:

的循环
Function MyTrim(s As String) As String
    Do While InStr(s, "  ") > 0
        s = Replace$(s, "  ", " ")
    Loop
    MyTrim = Trim$(s)
End Function

我试过这个方法"worked",但是痛苦慢:

Len In:  44930886 
Len Out: 35322469
Runtime: 247.6 seconds

有没有更快的方法从 "very large" 字符串中删除白色 space?

在VBA中,String的大小被限制在大约2 Billion Characters。上面的“Replace-Loop”方法4500万个字符串耗时247秒,超过4分钟

理论上,这意味着一个 20 亿个字符串至少需要 3 个小时——如果它甚至在没有崩溃的情况下完成的话——所以它并不完全实用。

Excel 有一个内置的工作表函数 Trim which is not the same as VBA's Trim 函数。

工作表函数 Trim 删除文本中的所有空格,单词之间的单个空格除外。

问题Trim,像所有用 Application.WorksheetFunction 调用的函数一样,有一个大小限制 32,767 个字符,并且这个 [不幸的是] 适用 即使从 VBA 调用函数时使用 甚至不在单元格中的字符串 .

但是,如果我们使用它来分段循环我们的 "gigantic string",我们仍然可以使用该函数,如下所示:

EDIT: Don't even bother with this crap (my function, below)! See the RegEx answer .

Function bigTrim(strIn As String) As String

    Const maxLen = 32766
    Dim loops As Long, x As Long
    loops = Int(Len(strIn) / maxLen)
    If (Len(strIn) / maxLen) <> loops Then loops = loops + 1

    For x = 1 To loops
        bigTrim = bigTrim & _
            Application.WorksheetFunction.Trim(Mid(strIn, _
            ((x - 1) * maxLen) + 1, maxLen))
    Next x

End Function

运行 这个函数在与“Replace-Loop”方法一起使用的同一字符串上产生了更好的结果:

Len In:  44930886 
Len Out: 35321845
Runtime: 33.6 seconds

这比“Replace-Loop”方法快 7 倍多, 设法删除了 624 个空格,这些空格不知何故被对方遗漏了方法。

(虽然我正在研究 为什么 第一种方法遗漏了字符,但因为我知道我的字符串没有遗漏任何东西,而这个的重点运动是为了节省时间,那就傻了!)

我怀疑性能问题是由于创建了大量的大中间字符串造成的。因此,任何在不创建中间字符串或使用更少的中间字符串的情况下执行操作的方法都会表现得更好。

正则表达式替换很有可能。

Option Explicit

Sub Test(ByVal text As String)

  Static Regex As Object
  If Regex Is Nothing Then
    Set Regex = CreateObject("VBScript.RegExp")
    Regex.Global = True
    Regex.MultiLine = True
  End If

  Regex.Pattern = " +" ' space, one or more times

  Dim result As String: result = Regex.Replace(text, " ")
  Debug.Print Len(result), Left(result, 20)
End Sub

输入 4500 万个字符的字符串大约需要一秒钟。

亚军:

Sub Main()

  Const ForReading As Integer = 1
  Const FormatUTF16 As Integer = -1 ' aka TriStateTrue
  Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
  Dim file As Object: Set file = fso.OpenTextFile("C:\ProgramData\test.txt", ForReading, False, FormatUTF16)
  Dim text As String: text = file.ReadAll()
  Set file = Nothing
  Set fso = Nothing
  Debug.Print Len(text), Left(text, 20)

  Test (text)

End Sub

测试数据创建者(C#):

var substring = "××\n× ××   ";
var text = String.Join("", Enumerable.Repeat(substring, 45_000_000 / substring.Length));
var encoding = new UnicodeEncoding(false, false);
File.WriteAllText(@"C:\ProgramData\test.txt", text, encoding);

BTW—因为 VBA (VB4, Java, JavaScript, C#, VB, …) 使用 UTF-16, space字符是一个UTF-16编码单元ChrW(32)。 (与 ASCII 的任何相似或比较,都是不必要的心理体操,如果将代码作为 ANSI [Chr(32)],在幕后进行不必要的转换,对于不同的机器、用户和时间具有不同的行为。)