【问题标题】:Quickly remove unnecessary whitespace from a (very large) string从(非常大的)字符串中快速删除不必要的空格
【发布时间】:2018-07-21 03:25:43
【问题描述】:

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

一个空格(又名ASCII Code 32)是可以的,但是任何有两个或多个连续空格的部分都应该减少到只有一个。

我发现了一个类似的问题here,尽管 OP 对“非常长的字符串”的定义只有 39,000 个字符。接受的答案是使用Replace 的循环:

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

我尝试了这种方法,它“有效”,但是痛苦地很慢:

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

有没有更快的方法从“非常大”的字符串中删除空格?

【问题讨论】:

    标签: excel vba performance trim removing-whitespace


    【解决方案1】:

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

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

    Excel 有一个内置的工作表函数Trim,它与VBA's Trim 函数不同

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

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

    但是,如果我们使用它来循环遍历我们的“巨大字符串”部分,我们仍然可以使用该函数,如下所示:

    编辑: 别管这个废话(我的功能,下面)! 查看 RegEx 答案above.

    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 个空格。

    (我虽然想研究 why 第一种方法遗漏了字符,但是因为我知道我的字符串没有遗漏任何东西,而且这个练习的目的是节省时间,所以会很傻!)

    【讨论】:

    • 您的解决方案存在 2 个问题。如果一系列空白由Mid 分隔,并且字符串连接是一种表达性操作,不应在循环中使用,则它将不起作用。您可以在大约一秒钟内使用正则表达式轻松修剪 40000000 个字符的字符串:re.Pattern = "\s+"Trim(re.Replace(strIn, " "))
    • @FlorentB。这听起来像是一个答案。
    • OMG *我什至没有想到 RegEx (可能部分是因为我过去没有太多运气弄清楚它!)我现在会尝试回来报告...!
    • 好吧,这已经在我的最爱列表中了。
    【解决方案2】:

    我怀疑性能问题是由于创建了大量的大型中间字符串。因此,任何不创建中间字符串或使用更少的字符串的方法都会表现得更好。

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

    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);
    

    顺便说一句——由于 VBA(VB4、Java、JavaScript、C#、VB 等)使用 UTF-16,因此空格字符是一个 UTF-16 代码单元 ChrW(32)。 (任何与 ASCII 的相似或比较都是不必要的脑力操,如果放入代码为 ANSI [Chr(32)],则在幕后进行不必要的转换,对于不同的机器、用户和时间会有不同的行为。)

    【讨论】:

    • 非常棒,您的Test 程序效果惊人:44930886 bytes in 1.6 seconds。我发现使用您的方法打开 FileSystemObject [在我将FormatUTF16 更改为1 之后;与我的文件一起使用的唯一选项] 大约是好 ol'Open fName For Input As #1: myText= Input(LOF(1), #1): Close #1 的两倍,但这几秒钟是无关紧要的,因为我的问题是关于 4 小时运行时间!我很感激;这为我的整个项目打开了一扇新的大门……谢谢! (也给@florent-b!)
    • (这是我自己回答的第一个问题,但结果比我预期的要好......我应该将自己的答案“标记”为“lame”?哈哈)
    猜你喜欢
    • 1970-01-01
    • 2019-02-04
    • 2022-07-24
    • 2014-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-08
    • 2015-11-27
    相关资源
    最近更新 更多