实现了对超长字符串的大小写不敏感比较,并通过不同的方法对性能进行了比较测试,包括使用系统 API、机器码和纯 AHK 遍历的方法。

 

#MaxMem 4095
SetBatchLines -1
; 比较字符串不区分大小写(并获得不同的基于 1 的字符索引)
vText1 := StrReplace(Format("{:241001000}", ""), " ", "a")
; MsgBox % vText1  ; 生成241001000个字符串a
vText2 := vText1
vText1 .= "A"
vText2 .= "b"
;vText2 .= "a"


; https://www.autohotkey.com/boards/viewtopic.php?p=297807#p297807
计时()
vRet := StrCmpI(vText1, vText2,, vOffsetDiff)
MsgBox, , 调用WinAPI的RtlCompareMemory比较, % "耗时:" 计时() "ms`n发现差异的字符位置:" vOffsetDiff "`n返回值:" vRet


; C语言机器码纯遍历,缺少分块优化
计时()
MyFunction := MCode("1,x64:33C04C8BC94D85C07E1E4C2BCA0F1F000FB70A48FFC06641390C11750B4883C202493BC07CEA33C0C3")
MsgBox, , 机器码c纯遍历, % "发现差异的字符位置:" DllCall(MyFunction, "Str", vText1, "Str", vText2, "int64", 241001000+1, "int64") "`n耗时:" 计时() "ms"


; 此项仅为了对比效率差异
计时()
vOffsetDiff := qSortErrorLookup(vText1, vText2)
MsgBox, , ahk纯Loop遍历检测, % "耗时:" 计时() "ms`n发现差异的字符位置:" vOffsetDiff
return



计时() {
  Static
  if 开始=
    DllCall("QueryPerformanceFrequency", "Int64*", 频率), DllCall("QueryPerformanceCounter", "Int64*", 开始)
   else
    DllCall("QueryPerformanceCounter", "Int64*", 结束), 耗时:=(结束 - 开始)/频率*1000, 开始:=""
  Return 耗时
}


;principles:
;use _wcsnicmp/_strnicmp to compare blocks of characters, find the first difference
;use memmove to copy blocks
;use CharLowerBuff to make them lower case
;use RtlCompareMemory to compare characters, find the first difference (its char index)

;note: vOffsetParam is for use with comparator functions for AHK's Sort command
;note: vText1/vText2 are ByRef for better performance (to avoid copying massive strings)
StrCmpI(ByRef vText1, ByRef vText2, vOffsetParam:=0, ByRef vOffsetOut:=0) {
  local
  static vChrSize := A_IsUnicode ? 2 : 1
  static vFunc := A_IsUnicode ? "msvcrt\_wcsnicmp" : "msvcrt\_strnicmp"
  static vBlockLen := 1000000
  vLen1 := StrLen(vText1)
  , vLen2 := StrLen(vText2)
  if !(vLen := Min(vLen1, vLen2))
    return -vOffsetParam ;return 0/-vOffsetParam if both empty strings
  vRem := Mod(vLen, vBlockLen)
  , vOffset := vOffsetOut := vRet := 0
  Loop % Floor(vLen/vBlockLen) {
    if vRet := DllCall(vFunc, "Ptr",&vText1+vOffset*vChrSize, "Ptr",&vText2+vOffset*vChrSize, "Ptr",vBlockLen, "Cdecl")
      break
    vOffset += vBlockLen
  }
  if !vRet && (vBlockLen := vRem)
    vRet := DllCall(vFunc, "Ptr",&vText1+vOffset*vChrSize, "Ptr",&vText2+vOffset*vChrSize, "Ptr",vRem, "Cdecl")
  if !vRet {
    if (vLen1 = vLen2)
      return -vOffsetParam ;return 0/-vOffsetParam if equal
    vOffsetOut := vLen + 1
    return (vLen1 > vLen2) ? 1 : -1
  }
  VarSetCapacity(vTemp1, vBlockLen*vChrSize)
  , VarSetCapacity(vTemp2, vBlockLen*vChrSize)
  , DllCall("msvcrt\memmove", "Ptr",&vTemp1, "Ptr",&vText1+vOffset*vChrSize, "UPtr",vBlockLen*vChrSize, "Cdecl Ptr")
  , DllCall("msvcrt\memmove", "Ptr",&vTemp2, "Ptr",&vText2+vOffset*vChrSize, "UPtr",vBlockLen*vChrSize, "Cdecl Ptr")
  , DllCall("user32\CharLowerBuff", "Ptr",&vTemp1, "UInt",vBlockLen, "UInt")
  , DllCall("user32\CharLowerBuff", "Ptr",&vTemp2, "UInt",vBlockLen, "UInt")
  , vOffset2 := DllCall("ntdll\RtlCompareMemory", "Ptr",&vTemp1, "Ptr",&vTemp2, "UPtr",vBlockLen*vChrSize, "UPtr")
  ;note: use Floor, since, for a Unicode string, the first byte that differs could be at an odd offset:
  , vOffsetOut := vOffset + Floor(vOffset2/vChrSize) + 1
  return (vRet > 0) ? 1 : -1
}


; ahk纯遍历检测函数
qSortErrorLookup(byref string1, byref string2, partCount := 2) {
  Local
  if (string1 = string2)
    Return false

  cLeftBorder := 1
  , cLen := Max(StrLen(string1), StrLen(string2))
  Loop {
    stepSize := (cLen)/partCount
    , leftBorder := cLeftBorder
    Loop %partCount% {
      leftB := Round(leftBorder)
      , len := Round((leftBorder+=stepSize)-leftB)
      if (SubStr(string1, leftB, len) != SubStr(string2, leftB, len)) {
        if (len = 1)
          Return leftB
        cLeftBorder := leftB
        , cLen := len
        Break
      }
    }
  }
  Return false
}


MCode(mcode) {
  static e := {1:4, 2:1}, c := (A_PtrSize=8) ? "x64" : "x86"
  if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", m))
    return
  if (!DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", 0, "uint*", s, "ptr", 0, "ptr", 0))
    return
  p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
  if (c="x64")
    DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", op)
  if (DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", p, "uint*", s, "ptr", 0, "ptr", 0))
    return p
  DllCall("GlobalFree", "ptr", p)
}

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。