Поиск строки в разных кодировках

Тема в разделе "WASM.A&O", создана пользователем M0rg0t, 2 фев 2021.

  1. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Представим такую задачу: есть тысячи текстовых файлов, нужно отсортировать те, в которых встречаются строки , к примеру "кот" или "кошка". Нюанс в том, что строки могут быть в кодировке win-1251 или utf-8 / utf-16LE. Как тут лучше сделать? Открываем файл , читаем в буфер (или маппим), ищем в анси, потом утф8, потом 16. Или лучше определять сначала кодировку файла (но Рихтер говорит, что функция IsUnicode может нестабильно работать, а ВОМ не везде есть). Ваши мысли?
     
  2. Carnival

    Carnival New Member

    Публикаций:
    0
    Регистрация:
    31 дек 2020
    Сообщения:
    26
    Адрес:
    ::1/128
    Маппить должно быть быстрее. Проверять если есть метка, то запускать только UTF-8/UTF-16 версию. Иначе же в цикле быстро искать первый байт/символ, а затем уже проверять через memcmp (чтобы обязательно сравнивала по блокам по возможности). Но это самое банальное, мб есть что-то поумнее.
     
    M0rg0t нравится это.
  3. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.241
    https://github.com/madx/moreutils/blob/master/isutf8.c
    https://github.com/chardet/chardet
    сначала сортируй файлы по кодировкам, а затем ужо содержимое копай.
     
    M0rg0t нравится это.
  4. R81...

    R81... Active Member

    Публикаций:
    0
    Регистрация:
    1 фев 2020
    Сообщения:
    153
    То что сказали выше и первично RepNE ScasB или RepNE ScasW для 16бит. Не знаю что там в memcmp, но это должно бы называться memScas, в отличии от CmpS.
     
    M0rg0t нравится это.
  5. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Я предполагаю, что мы говорим о венде, раз обсуждение идет win-1251 и utf-16le. В advapi32.dll есть интересная функция: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-istextunicode - я не в курсе, как именно она работает, но, вероятно, она способна как минимум utf-16le отличить. Есть еще такой ком-интерфейс (я знаю, что у тебя фобия ком-интерфейсов, но может тоже пригодится): https://docs.microsoft.com/en-us/pr...rm-apis/aa740986(v=vs.85)?redirectedfrom=MSDN
     
    M0rg0t нравится это.
  6. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Я бы сделал так - создал искомую строку во всех кодировках, потом уже в каждом файле вел поиск по первому символу. Если символ найден - то проверять уже найденную строку. Если строка найдена то уже проверять кодировку файла. Определять можно через MLang к примеру. Мне когда нужен был детект кодировки я делал как в блокноте - вручную чекал. Вот код класса CTextFile на VB6:
    Код (Text):
    1. Option Explicit
    2.  
    3. Public Enum eTextEncoding
    4.     TE_ANSI = 0
    5.     TE_UNICODE = 1
    6.     TE_UTF8 = 2
    7.     TE_CPMASK = 3
    8.     TE_BIGENDIAN = 4
    9.     TE_HASBOM = 8
    10. End Enum
    11.  
    12. Private m_sContent  As String
    13. Private m_eEncoding As eTextEncoding
    14. Private m_sFileName As String
    15.  
    16. Public Property Get Encoding() As eTextEncoding
    17.     Encoding = m_eEncoding
    18. End Property
    19. Public Property Let Encoding( _
    20.                     ByVal eValue As eTextEncoding)
    21.     m_eEncoding = eValue
    22. End Property
    23.  
    24. Public Property Get Content() As String
    25.     Content = m_sContent
    26. End Property
    27. Public Property Let Content( _
    28.                     ByRef sValue As String)
    29.     m_sContent = sValue
    30. End Property
    31.  
    32. Public Property Get FileName() As String
    33.     FileName = m_sFileName
    34. End Property
    35. Public Property Let FileName( _
    36.                     ByRef sValue As String)
    37.     m_sFileName = sValue
    38. End Property
    39.  
    40. Public Function SaveTextFile( _
    41.                 ByRef sFileName As String) As Boolean
    42.     Dim bData()     As Byte
    43.     Dim hFile       As OLE_HANDLE
    44.     Dim lSize       As Long
    45.     Dim lIndex      As Long
    46.     Dim lSymIdx     As Long
    47.     Dim pString     As Long
    48.     Dim lChar       As Long
    49.     Dim lTotalSize  As Long
    50.     Dim lCodePage   As Long
    51.    
    52.     On Error GoTo CleanUp
    53.    
    54.     hFile = CreateFile(sFileName, GENERIC_WRITE Or GENERIC_READ, 0, ByVal 0&, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
    55.     If hFile = INVALID_HANDLE_VALUE Then
    56.         Err.Raise 7, "CTextFile::SaveTextFile", "CreateFile failed"
    57.     End If
    58.    
    59.     Select Case (m_eEncoding And TE_CPMASK)
    60.     Case TE_ANSI, TE_UTF8
    61.        
    62.         If (m_eEncoding And TE_CPMASK) = TE_ANSI Then
    63.             lCodePage = CP_ACP
    64.         Else
    65.        
    66.             lCodePage = CP_UTF8
    67.                
    68.             If m_eEncoding And TE_HASBOM Then
    69.                 lTotalSize = 3: lIndex = 3
    70.             End If
    71.        
    72.         End If
    73.  
    74.         If Len(m_sContent) Then
    75.        
    76.             lSize = WideCharToMultiByte(lCodePage, 0, ByVal StrPtr(m_sContent), Len(m_sContent), ByVal 0&, 0, ByVal 0&, 0)
    77.             If lSize = 0 Then
    78.                 Err.Raise 7, "CTextFile::SaveTextFile", "WideCharToMultiByte failed"
    79.             End If
    80.            
    81.             lTotalSize = lTotalSize + lSize
    82.            
    83.             ReDim bData(lTotalSize - 1)
    84.            
    85.             If WideCharToMultiByte(lCodePage, 0, ByVal StrPtr(m_sContent), Len(m_sContent), bData(lIndex), lSize, ByVal 0&, 0) = 0 Then
    86.                 Err.Raise 7, "CTextFile::SaveTextFile", "WideCharToMultiByte failed"
    87.             End If
    88.  
    89.         End If
    90.        
    91.         If (m_eEncoding And TE_HASBOM) And ((m_eEncoding And TE_CPMASK) = TE_UTF8) Then
    92.             bData(0) = &HEF
    93.             bData(1) = &HBB
    94.             bData(2) = &HBF
    95.         End If
    96.  
    97.     Case TE_UNICODE
    98.        
    99.         lSize = LenB(m_sContent)
    100.    
    101.         If m_eEncoding And TE_HASBOM Then
    102.             lTotalSize = lSize + 2
    103.         Else
    104.             lTotalSize = lSize
    105.         End If
    106.        
    107.         If lTotalSize > 0 Then
    108.        
    109.             ReDim bData(lTotalSize - 1)
    110.            
    111.             If m_eEncoding And TE_HASBOM Then
    112.                 If m_eEncoding And TE_BIGENDIAN Then
    113.                     GetMem2 &HFFFE&, bData(0)
    114.                 Else
    115.                     GetMem2 &HFEFF&, bData(0)
    116.                 End If
    117.                
    118.                 lIndex = lIndex + 2
    119.                
    120.             End If
    121.            
    122.             If m_eEncoding And TE_BIGENDIAN Then
    123.                
    124.                 pString = StrPtr(m_sContent)
    125.            
    126.                 For lSymIdx = 0 To Len(m_sContent) - 1
    127.                     GetMem2 ByVal pString + lSymIdx * 2, lChar
    128.                     lChar = (lChar \ &H100) Or ((lChar And &HFF) * &H100)
    129.                     GetMem2 lChar, bData(lIndex + lSymIdx * 2)
    130.                 Next
    131.                
    132.             Else
    133.                 memcpy bData(lIndex), ByVal StrPtr(m_sContent), LenB(m_sContent)
    134.             End If
    135.            
    136.         End If
    137.        
    138.     End Select
    139.      
    140.     If lTotalSize > 0 Then
    141.    
    142.         If WriteFile(hFile, bData(0), lTotalSize, lSize, ByVal 0&) = 0 Then
    143.             Err.Raise 7, "CTextFile::SaveTextFile", "WriteFile failed"
    144.         End If
    145.        
    146.         If lSize = lTotalSize Then
    147.             SaveTextFile = True
    148.         End If
    149.        
    150.     Else
    151.         SaveTextFile = True
    152.     End If
    153.    
    154.     m_sFileName = sFileName
    155.    
    156. CleanUp:
    157.    
    158.     CloseHandle hFile
    159.    
    160.     If Err.Number Then
    161.         Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
    162.     End If
    163.    
    164. End Function
    165.  
    166. ' //
    167. ' // Load file and convert it to UTF-16
    168. ' //
    169. Public Sub LoadTextFile( _
    170.            ByRef sFileName As String)
    171.     Dim hFile   As OLE_HANDLE
    172.     Dim hMap    As OLE_HANDLE
    173.     Dim pData   As Long
    174.     Dim liSize  As LARGE_INTEGER
    175.    
    176.     hFile = CreateFile(sFileName, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
    177.     If hFile = INVALID_HANDLE_VALUE Then
    178.         Err.Raise 7, "CTextFile::LoadTextFile", "CreateFile failed"
    179.     End If
    180.    
    181.     If GetFileSizeEx(hFile, liSize) = 0 Then
    182.         CloseHandle hFile
    183.         Err.Raise 7, "CTextFile::LoadTextFile", "GetFileSizeEx failed"
    184.     End If
    185.    
    186.     If liSize.HighPart <> 0 Or liSize.LowPart < 0 Or liSize.LowPart > 10000000 Then
    187.         CloseHandle hFile
    188.         Err.Raise 7, "CTextFile::LoadTextFile", "File is too big"
    189.     End If
    190.    
    191.     hMap = CreateFileMapping(hFile, ByVal 0&, PAGE_READONLY, 0, 0, vbNullString)
    192.     CloseHandle hFile
    193.     If hMap = 0 Then
    194.         Err.Raise 7, "CTextFile::LoadTextFile", "CreateFileMapping failed"
    195.     End If
    196.    
    197.     pData = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)
    198.     CloseHandle hMap
    199.     If pData = 0 Then
    200.         Err.Raise 7, "CTextFile::LoadTextFile", "MapViewOfFile failed"
    201.     End If
    202.    
    203.     On Error GoTo CleanUp
    204.    
    205.     LoadFromMemory pData, liSize.LowPart
    206.    
    207.     m_sFileName = sFileName
    208.    
    209. CleanUp:
    210.    
    211.     UnmapViewOfFile ByVal pData
    212.      
    213.     If Err.Number Then
    214.         Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
    215.     End If
    216.      
    217. End Sub
    218.  
    219. Private Function LoadFromMemory( _
    220.                  ByVal pData As Long, _
    221.                  ByVal lSize As Long) As String
    222.     Dim eEncoding   As eTextEncoding
    223.     Dim sRet        As String
    224.     Dim lRetSize    As Long
    225.     Dim lIndex      As Long
    226.     Dim lCodePage   As Long
    227.     Dim lSwap       As Long
    228.     Dim pRet        As Long
    229.    
    230.     If lSize = 0 Then Exit Function
    231.    
    232.     eEncoding = DetectEncoding(pData, lSize)
    233.  
    234.     If (eEncoding And TE_CPMASK) = TE_UNICODE Then
    235.        
    236.         If eEncoding And TE_HASBOM Then
    237.             pData = pData + 2
    238.             lSize = lSize - 2
    239.         End If
    240.        
    241.         sRet = Space$((lSize + 1) \ 2)
    242.        
    243.         If eEncoding And TE_BIGENDIAN Then
    244.            
    245.             pRet = StrPtr(sRet)
    246.            
    247.             For lIndex = 0 To Len(sRet) - 1
    248.                
    249.                 GetMem2 ByVal pData + lIndex * 2, lSwap
    250.                 lSwap = ((lSwap And &HFF) * &H100) Or (lSwap \ &H100)
    251.                 GetMem2 lSwap, ByVal pRet + lIndex * 2
    252.                
    253.             Next
    254.            
    255.         Else
    256.             memcpy ByVal StrPtr(sRet), ByVal pData, lSize
    257.         End If
    258.        
    259.     Else
    260.        
    261.         If (eEncoding And TE_CPMASK) = TE_ANSI Then
    262.             lCodePage = CP_ACP
    263.         ElseIf (eEncoding And TE_CPMASK) = TE_UTF8 Then
    264.            
    265.             lCodePage = CP_UTF8
    266.            
    267.             If eEncoding And TE_HASBOM Then
    268.                 pData = pData + 3
    269.                 lSize = lSize - 3
    270.             End If
    271.            
    272.         End If
    273.        
    274.         lRetSize = MultiByteToWideChar(lCodePage, 0, ByVal pData, lSize, ByVal 0&, 0)
    275.         If lRetSize = 0 Then
    276.             Err.Raise 7, "CTextFile::LoadFromMemory", "MultiByteToWideChar failed"
    277.         End If
    278.        
    279.         sRet = Space$(lRetSize)
    280.        
    281.         If MultiByteToWideChar(lCodePage, 0, ByVal pData, lSize, ByVal StrPtr(sRet), lRetSize) = 0 Then
    282.             Err.Raise 7, "CTextFile::LoadFromMemory", "MultiByteToWideChar failed"
    283.         End If
    284.        
    285.     End If
    286.    
    287.     m_sContent = sRet
    288.     m_eEncoding = Encoding
    289.    
    290. End Function
    291.  
    292. Private Function DetectEncoding( _
    293.                  ByVal pData As Long, _
    294.                  ByVal lSize As Long) As eTextEncoding
    295.     Dim lBOM    As Long
    296.     Dim bBE     As Boolean
    297.    
    298.     If lSize < 2 Then
    299.         DetectEncoding = TE_ANSI
    300.         Exit Function
    301.     End If
    302.    
    303.     GetMem2 ByVal pData, lBOM
    304.    
    305.     If lBOM = &HFEFF& Then
    306.         ' // UTF-16 LE
    307.         DetectEncoding = TE_UNICODE Or TE_HASBOM
    308.     ElseIf lBOM = &HFFFE& Then
    309.         ' // UTF-16 BE
    310.         DetectEncoding = TE_UNICODE Or TE_BIGENDIAN Or TE_HASBOM
    311.     ElseIf lSize > 2 Then
    312.         If lBOM = &HBBEF& Then
    313.            
    314.             GetMem1 ByVal pData + 2, lBOM
    315.            
    316.             If (lBOM And &HFF) = &HBF Then
    317.                 ' // UTF-8
    318.                 DetectEncoding = TE_UTF8 Or TE_HASBOM
    319.             End If
    320.            
    321.         Else
    322.             If IsInputTextUnicode(pData, lSize, bBE) Then
    323.                 ' // UTF-16
    324.                 If bBE Then
    325.                     DetectEncoding = TE_UNICODE Or TE_BIGENDIAN
    326.                 Else
    327.                     DetectEncoding = TE_UNICODE
    328.                 End If
    329.             ElseIf IsInputTextUTF8(pData, lSize) Then
    330.                 ' // UTF-8
    331.                 DetectEncoding = TE_UTF8
    332.             Else
    333.                 ' // ANSI
    334.                 DetectEncoding = TE_ANSI
    335.             End If
    336.         End If
    337.     Else
    338.         DetectEncoding = TE_ANSI
    339.     End If
    340.                    
    341. End Function
    342.  
    343. Private Function IsInputTextUTF8( _
    344.                  ByVal pData As Long, _
    345.                  ByVal lSize As Long) As Boolean
    346.     Dim bChar   As Byte
    347.     Dim lIndex  As Long
    348.     Dim bNoHigh As Boolean
    349.     Dim lCount  As Long
    350.    
    351.     If lSize <= 0 Then Exit Function
    352.    
    353.     bNoHigh = True
    354.    
    355.     For lIndex = 0 To lSize - 1
    356.    
    357.         GetMem1 ByVal pData + lIndex, bChar
    358.        
    359.         If (bChar And &H80) <> 0 Then
    360.             bNoHigh = False
    361.         End If
    362.            
    363.         If lCount Then
    364.            
    365.             If (bChar And &HC0) <> &H80 Then
    366.                 Exit Function
    367.             End If
    368.            
    369.             lCount = lCount - 1
    370.            
    371.         ElseIf bChar >= &H80 Then
    372.        
    373.             Do
    374.                
    375.                 bChar = (CLng(bChar) * 2) And &HFF
    376.                 lCount = lCount + 1
    377.                
    378.             Loop While bChar And &H80
    379.            
    380.             lCount = lCount - 1
    381.            
    382.             If lCount = 0 Then
    383.                 Exit Function
    384.             End If
    385.            
    386.         End If
    387.        
    388.     Next
    389.    
    390.     If CBool(lCount) Or bNoHigh Then
    391.         Exit Function
    392.     Else
    393.         IsInputTextUTF8 = True
    394.     End If
    395.    
    396. End Function
    397.  
    398. Private Function IsInputTextUnicode( _
    399.                  ByVal pData As Long, _
    400.                  ByVal lSize As Long, _
    401.                  ByRef bIsBigEndian As Boolean) As Boolean
    402.     Dim lFlags  As Long
    403.    
    404.     lFlags = -1
    405.    
    406.     If IsTextUnicode(ByVal pData, lSize, lFlags) Then
    407.         If lSize < 100 And lFlags = IS_TEXT_UNICODE_STATISTICS Then
    408.             IsInputTextUnicode = False
    409.         Else
    410.        
    411.             If lFlags = IS_TEXT_UNICODE_REVERSE_STATISTICS Then
    412.                 bIsBigEndian = True
    413.             Else
    414.                 bIsBigEndian = False
    415.             End If
    416.            
    417.             IsInputTextUnicode = True
    418.            
    419.         End If
    420.     End If
    421.    
    422. End Function
    423.  
    424.  
    Обрати внимание как реализован метод LoadTextFile. По MLang тоже есть пример, но тоже на VB6.
     
    Aiks и M0rg0t нравится это.
  7. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Спасибо всем за ответы.
    да не то чтобы фобия, не понимаю я их и все. ну такое, мб разберусь.

    апи видел, но про нее Рихтер и говорит, что может нестабильно работать , мол чем больше буфер дать на вход, тем больше вероятность успеха.
     
  8. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.330
    Ну правильно, чем больше байт в буффере, тем больше вероятность, что в нем встретятся байты или цепочки байт, специфичные какой-то кодировке, так все автодетекты кодировок работают.