tubaba 发表于 2016-10-19 16:23:47

将中文字符放入结构体中的心得

本帖最后由 tubaba 于 2016-10-19 16:29 编辑

不知道大家是否翻看过坛子上的一篇贴子.
发现_GUICtrlListView_FindText搜索有个BUG,不支持中文完全匹配(已解决)
发现_GUICtrlListView_FindText搜索有个BUG,不支持中文完全匹配,问题续(已解决)

里面就涉及到将中文放入C/C++ 数据结构中的问题.其中Afan版主是这样解决的.      Local $aSR = StringRegExp($sText, '[^\x00-\xff]', 3), $iL = UBound($aSR)
      Local $iBuffer = StringLen($sText) + 1 + $iL
      Local $tBuffer = DllStructCreate('char Text[' & $iBuffer & ']')
      Local $pBuffer = DllStructGetPtr($tBuffer)
      For $i = 0 To $iL - 2
                $sText &= ' '
      Next
      DllStructSetData($tBuffer, 'Text', $sText)这果然是一个好办法.中文可以正确放入与取出了.但是最近我在写myscitejump的搜索中文功能时发现,如果编辑器内容是ansi编码,用这种方法没有问题.但是如果是utf8,那就不行了.在scintilla开发文档中,指出SCI_SEARCHINTARGET(int length, const char *text) → int
This searches for the first occurrence of a text string in the target defined by SCI_SETTARGETSTART and SCI_SETTARGETEND. The text string is not zero terminated; the size is set by length. The search is modified by the search flags set by SCI_SETSEARCHFLAGS. If the search succeeds, the target is set to the found text and the return value is the position of the start of the matching text. If the search fails, the result is -1.

必须将查询的关键字放入类型为char的结构体中.这就出现了一个问题.如何将"unicode字符放入char结构",这个问题困扰了我整一个星期.如何才能在scintilla中搜索中文.文档是ansi,用afan版主的方法是可行的.但是在utf8编码中,行不通了.猜想是中文ansi与unicode编码不一样造成的.在网上到处找资料.可惜现在研究AU3的人太少了.也没得到啥子结果.最后在翻看AU3帮助文档DllStructCreate中,突然灵光一闪.byte与char都是8位字符,区别在于一个是无符号,一个是ascII.
尝试创建byte结构,结果居然成功了!

下面将示例代码发出来,我想这一定能解决很多中文放入结构体的问题,将文本转换为二进制,至于长度不用特别计算.管它是中文还是英文,要关心的是ANSI编码还是UNICODE编码,然后直接用binarylen得到字节长度.
这样就不需要有几个汉字留几个空让其截断了,我认为应该比上面的办法更好一些{:face (356):}Func _Sci_FindInTarget($SerchText, $iStart, $iEnd) ;查找目标        ;消息原型.SCI_SEARCHINTARGET(int length, const char *text) → int
        Local $sText = ''
        Local $Codepage = _Sci_GetCodepage()
        Switch $Codepage
                Case 65001
                        $sText = StringToBinary($SerchText, 4)        ;编码为UTF8
                Case Else
                        $sText = StringToBinary($SerchText, 1)        ;编码为ANSI
        EndSwitch
        Local $searchTextLen = BinaryLen($sText)
        Local $tBuffer = DllStructCreate('byte buffer[' & $searchTextLen & ']') ;建立字节结构,unicode一个汉字占用3个字节,在这里使用byte,而不是char
        Local $iBuffer = DllStructGetSize($tBuffer)
        DllStructSetData($tBuffer, 'buffer', $sText) ;以字节的方式放入结构中,保证文本的完整性.否则可能在转换的过程中造成丢失
        _Sci_SetTargetStart($iStart)
        _Sci_SetTargetEnd($iEnd)
        Local $tMemMap
        Local $pMemory = _MemInit($__vSciTEAPI[$__hSciTEEdit], $iBuffer, $tMemMap) ;初始化内存分区 在目标进程划分大小为$iBuffer的内存
        _MemWrite($tMemMap, $tBuffer) ;复制结构
        Local $PosFind = _Sci_SearchInTarget($iBuffer, $pMemory)
        If $PosFind = -1 Then
                _Sci_SetTargetStart(0)
                _Sci_SetTargetEnd($iStart - 1)
                $PosFind = _Sci_SearchInTarget($iBuffer, $pMemory)
        EndIf
        Return $PosFind
EndFunc   ;==>_Sci_FindInTarget同理,Afan版主的函数也可以这样修改Func __GUICtrlListView_FindItem($hWnd, $iStart, ByRef $tFindInfo, $sText = "")
        Local $sBinText = StringToBinary($sText, 1)
        Local $iBuffer = BinaryLen($sBinText)+1
        Local $tBuffer = DllStructCreate("byte Text[" & $iBuffer & "]")
        Local $pBuffer = DllStructGetPtr($tBuffer)
        DllStructSetData($tBuffer, "Text", $sBinText)
        Local $iRet
        If IsHWnd($hWnd) Then
                If _WinAPI_InProcess($hWnd, $__g_hLVLastWnd) Then
                        DllStructSetData($tFindInfo, "Text", $pBuffer)
                        $iRet = _SendMessage($hWnd, $LVM_FINDITEM, $iStart, $tFindInfo, 0, "wparam", "struct*")
                Else
                        Local $iFindInfo = DllStructGetSize($tFindInfo)
                        Local $tMemMap
                        Local $pMemory = _MemInit($hWnd, $iFindInfo + $iBuffer, $tMemMap)
                        Local $pText = $pMemory + $iFindInfo
                        DllStructSetData($tFindInfo, "Text", $pText)
                        _MemWrite($tMemMap, $tFindInfo, $pMemory, $iFindInfo)
                        _MemWrite($tMemMap, $tBuffer, $pText, $iBuffer)
                        $iRet = _SendMessage($hWnd, $LVM_FINDITEM, $iStart, $pMemory, 0, "wparam", "ptr")
                        _MemFree($tMemMap)
                EndIf
        Else
                DllStructSetData($tFindInfo, "Text", $pBuffer)
                $iRet = GUICtrlSendMsg($hWnd, $LVM_FINDITEM, $iStart, DllStructGetPtr($tFindInfo))
        EndIf
        Return $iRet
EndFunc   ;==>_GUICtrlListView_FindItem开贴留做备忘

haijie1223 发表于 2016-10-19 23:36:30

afan原帖地址提供一下

haijie1223 发表于 2016-10-20 10:23:32

哦,原来如此,昨晚手机看贴,没注意到

131738 发表于 2016-10-20 10:58:07

本帖最后由 131738 于 2016-10-20 11:01 编辑

不知道大家是否翻看过坛子上的一篇贴子.



里面就涉及到将中文放入C/C++ 数据结构中的问题.其中Afan版 ...
tubaba 发表于 2016-10-19 16:23 http://www.autoitx.com/images/common/back.gif

非常不错的研究结果, 如果我以后还会发布 AutoIt 安装包的话, 决定据此修改包含文件 GuiListView.au3 中的函数 _GUICtrlListView_FindItem() 一并发布.................

kk_lee69 发表于 2016-10-20 10:59:43

回复 1# tubaba

這篇我覺得是不是應該放到 中文資料區不然放在這久了 可能就消失了....

可能要請版主 移動一下吧

留個腳印 macgyver

131738 发表于 2016-10-20 11:01:09

afan原帖地址提供一下
haijie1223 发表于 2016-10-19 23:36 http://www.autoitx.com/images/common/back.gif

应该就在 LZ 主贴给的链接贴中吧, 我也是现在才发现 _GUICtrlListView_FindItem() 确实不支持中文搜索的..............

haijie1223 发表于 2016-10-20 11:05:45

以前没有注意到这个函数,为什么搜索内容不能匹配第一个字符,就搜索不到呢?

haijie1223 发表于 2016-10-20 11:17:22

回复 6# 131738
测试了一下还发现这个问题:
#include <GuiListView.au3>

GUICreate("ListView Find Text", 400, 300)
$hListView = GUICtrlCreateListView("", 2, 2, 394, 268)
GUISetState()
_GUICtrlListView_AddColumn($hListView, "Items", 100)
_GUICtrlListView_BeginUpdate($hListView)
For $iI = 4 To 49
        _GUICtrlListView_AddItem($hListView, "en" & $iI)
Next
_GUICtrlListView_AddItem($hListView, "中文Target item")
For $iI = 51 To 100
        _GUICtrlListView_AddItem($hListView, "Item " & $iI)
Next
_GUICtrlListView_AddItem($hListView, "end")
_GUICtrlListView_EndUpdate($hListView)

; 搜索目标项目
$iI = __GUICtrlListView_FindText($hListView, "文", -1, 1)
MsgBox(4160, "1", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "文", -1, 0)
MsgBox(4160, "2", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "中", -1, 1)
MsgBox(4160, "3", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "中", -1, 0)
MsgBox(4160, "4", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "Target", -1, 1)
MsgBox(4160, "5", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "Target", -1, 0)
MsgBox(4160, "6", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)


Do
Until GUIGetMsg() = -3
GUIDelete()

Func __GUICtrlListView_FindText($hWnd, $sText, $iStart = -1, $fPartialOK = True, $fWrapOK = True)
        Local $tFindInfo = DllStructCreate($tagLVFINDINFO)
        Local $iFlags = $LVFI_STRING
;~         Local $iFlags = $LVFI_SUBSTRING
        If $fPartialOK Then $iFlags = BitOR($iFlags, $LVFI_SUBSTRING)
        If $fWrapOK Then $iFlags = BitOR($iFlags, $LVFI_WRAP)
        DllStructSetData($tFindInfo, "Flags", $iFlags)
        Return __GUICtrlListView_FindItem($hWnd, $iStart, $tFindInfo, $sText)
EndFunc   ;==>__GUICtrlListView_FindText

Func __GUICtrlListView_FindItem($hWnd, $iStart, ByRef $tFindInfo, $sText = "")
        Local $sBinText = StringToBinary($sText, 1)
        Local $iBuffer = BinaryLen($sBinText) + 1
        Local $tBuffer = DllStructCreate("byte Text[" & $iBuffer & "]")
        Local $pBuffer = DllStructGetPtr($tBuffer)
        DllStructSetData($tBuffer, "Text", $sBinText)
        Local $iRet
        If IsHWnd($hWnd) Then
                If _WinAPI_InProcess($hWnd, $__g_hLVLastWnd) Then
                        DllStructSetData($tFindInfo, "Text", $pBuffer)
                        $iRet = _SendMessage($hWnd, $LVM_FINDITEM, $iStart, $tFindInfo, 0, "wparam", "struct*")
                Else
                        Local $iFindInfo = DllStructGetSize($tFindInfo)
                        Local $tMemMap
                        Local $pMemory = _MemInit($hWnd, $iFindInfo + $iBuffer, $tMemMap)
                        Local $pText = $pMemory + $iFindInfo
                        DllStructSetData($tFindInfo, "Text", $pText)
                        _MemWrite($tMemMap, $tFindInfo, $pMemory, $iFindInfo)
                        _MemWrite($tMemMap, $tBuffer, $pText, $iBuffer)
                        $iRet = _SendMessage($hWnd, $LVM_FINDITEM, $iStart, $pMemory, 0, "wparam", "ptr")
                        _MemFree($tMemMap)
                EndIf
        Else
                DllStructSetData($tFindInfo, "Text", $pBuffer)
                $iRet = GUICtrlSendMsg($hWnd, $LVM_FINDITEM, $iStart, DllStructGetPtr($tFindInfo))
        EndIf
        Return $iRet
EndFunc   ;==>__GUICtrlListView_FindItem

Func __GUICtrlListView_FindItemAfan($hWnd, $iStart, ByRef $tFindInfo, $sText = '')
        Local $aSR = StringRegExp($sText, '[^\x00-\xff]', 3), $iL = UBound($aSR)
        Local $iBuffer = StringLen($sText) + 1 + $iL
        Local $tBuffer = DllStructCreate('char Text[' & $iBuffer & ']')
        Local $pBuffer = DllStructGetPtr($tBuffer)
        For $i = 0 To $iL - 2
                $sText &= ' '
        Next
        DllStructSetData($tBuffer, 'Text', $sText)
        Local $iRet
        If IsHWnd($hWnd) Then
                If _WinAPI_InProcess($hWnd, $__g_hLVLastWnd) Then
                        DllStructSetData($tFindInfo, 'Text', $pBuffer)
                        ConsoleWrite('222')
                        $iRet = _SendMessage($hWnd, $LVM_FINDITEM, $iStart, $tFindInfo, 0, 'wparam', 'struct*')
                Else
                        Local $iFindInfo = DllStructGetSize($tFindInfo)
                        Local $tMemMap
                        Local $pMemory = _MemInit($hWnd, $iFindInfo + $iBuffer, $tMemMap)
                        Local $pText = $pMemory + $iFindInfo
                        DllStructSetData($tFindInfo, 'Text', $pText)
                        _MemWrite($tMemMap, $tFindInfo, $pMemory, $iFindInfo)
                        _MemWrite($tMemMap, $tBuffer, $pText, $iBuffer)
                        ConsoleWrite('0000')
                        $iRet = _SendMessage($hWnd, $LVM_FINDITEM, $iStart, $pMemory, 0, 'wparam', 'ptr')
                        _MemFree($tMemMap)
                EndIf
        Else
                DllStructSetData($tFindInfo, 'Text', $pBuffer)
                ConsoleWrite('111111111')
                $iRet = GUICtrlSendMsg($hWnd, $LVM_FINDITEM, $iStart, DllStructGetPtr($tFindInfo))
        EndIf
        Return $iRet
EndFunc   ;==>__GUICtrlListView_FindItemAfan

cashiba 发表于 2016-10-20 11:59:17

虽然看不懂,还是顶一下.....{:face (332):}

131738 发表于 2016-10-20 12:17:44

本帖最后由 131738 于 2016-10-20 12:37 编辑

回复131738
测试了一下还发现这个问题:
haijie1223 发表于 2016-10-20 11:17 http://www.autoitx.com/images/common/back.gif

单汉字搜索失败, 返回 -1, 但单字母却可以, 这个就不懂了.......
afan 应该可以解决吧.........

tubaba 发表于 2016-10-20 12:36:37

本帖最后由 tubaba 于 2016-10-20 12:48 编辑

回复 8# haijie1223
刚才看了下函数文档,$bPartialOK [可选] 如为 True, 只要项目文本开头匹配 $sText 的开头, 则搜索成立 ,所以理解为分二种情况,一种是开头匹配,一种是完整匹配,所以想匹配中间的没门$iI = __GUICtrlListView_FindText($hListView, "文", -1, 1)        ;$bPartialOK =true ,第一个字不匹配,搜索不成立
MsgBox(4160, "1", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "文", -1, 0)        ;$bPartialOK =false,整个项目文本不匹配,搜索不成立
MsgBox(4160, "2", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

;~ $iI = __GUICtrlListView_FindText($hListView, "中", -1, 1)        ;$bPartialOK =true ,第一个字匹配,搜索成立
;~ MsgBox(4160, "3", "Target Item Index: " & $iI)
;~ If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "中", -1, 0)        ;$bPartialOK=false,整个项目文本不匹配,搜索不成立
MsgBox(4160, "4", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)f

$iI = __GUICtrlListView_FindText($hListView, "Target", -1, 1)        ;$bPartialOK =true ,第一个字不匹配,搜索不成立
MsgBox(4160, "5", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "Target", -1, 0)        ;$bPartialOK =false ,整个字不匹配,搜索不成立
MsgBox(4160, "6", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)3643]10# 131738

请教怎么个语法着色? 另外,myscitejump已经支持中文搜索.并将注释,控件命令搜索加入树视图.等下放出exe,请你测试

131738 发表于 2016-10-20 12:39:14

回复 11# tubaba

"请教怎么个语法着色?" 抱歉! 我不懂.........

tubaba 发表于 2016-10-20 12:47:07

回复 12# 131738


    sorry,没写清楚,就是haijie1223贴子里的代码引用部分,是经过语法着色的,而我发出来的是清一色的.怎么个弄法

131738 发表于 2016-10-20 12:47:21

回复 11# tubaba

"请教怎么个语法着色?" 抱歉! 我不懂.........
" $iI = __GUICtrlListView_FindText($hListView, "中", -1, 1)      ;$bPartialOK =true ,第一个字匹配,搜索成立"但返回索引似乎不对..........

tubaba 发表于 2016-10-20 12:57:31

回复 14# 131738


    因为For $iI = 4 To 49 :),他不是从一开始的....

另外你可以试下 $iI = __GUICtrlListView_FindText($hListView, "a", -1, 1)        ;$bPartialOK =true ,第一个字不匹配,搜索不成立
MsgBox(4160, "7", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "I", -1, 1)        ;$bPartialOK =true ,第一个字匹配,搜索成立
MsgBox(4160, "8", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)

$iI = __GUICtrlListView_FindText($hListView, "I", -1, 0)        ;$bPartialOK =false ,整个文本不匹配,搜索不成立
MsgBox(4160, "9", "Target Item Index: " & $iI)
If $iI <> -1 Then _GUICtrlListView_EnsureVisible($hListView, $iI)这个函数应该是没错的
页: [1] 2
查看完整版本: 将中文字符放入结构体中的心得