VB 打开包含Unicode UTF-8之类字符的文件名的文件

时间:2021-09-18 23:09:40
这是在几年前写文本批量处理时遇到的问题。百度至今未能解决,特来求大神指点。
问题如下。
当时程序写了个遍历目录的功能,然后却发现总有某几个文件打不开,调试后发现是文件名错误。
文件名是Unicoe或者UTF-8字符,或者是全部,或者是部分
最常见的就是其它字符可以识别,其中的空格变成了?
就好像  “VB 编程  论坛” 变成“VB?编程?论坛”
有些就是全部乱码
调试中发现,VB在获取文件名后自动转换,例如变量什么的,一点解决的办法也没有‘
要是文件内容存在编码问题的话,还可以使用字节转换,而文件名就无从下手了。
或者说,是不是也有一种办法可以从字节状态去操作文件名
哪怕识别判断一下,要是就修改成正常的,再去打开。

21 个解决方案

#1


帮顶同问,因为这个问题我也遇到了,一直没能解决……(比如含Alt+0160弄出来的这种空白字符的文件名)

#2


操作系统保存文件名不是用UFT-8的吧,至少在WINDOWS中的NTFS中我记得应该是UTF-16,这个用FSO处理时应该支持。

#3


引用 2 楼 bcrun 的回复:
操作系统保存文件名不是用UFT-8的吧,至少在WINDOWS中的NTFS中我记得应该是UTF-16,这个用FSO处理时应该支持。

不行的,已经试过N多方法了,用FSO,Dir,FindFirstFile等API来获取该文件名都获取不了!

#4


求大神,这个应该有解决办法,不然VB岂不是废了,想写个文本处理器都只能放弃了

#5


用fso的for each file in fso.getfolder("path").files方法也不能获取吗?

#6


引用 5 楼 Topc008 的回复:
用fso的for each file in fso.getfolder("path").files方法也不能获取吗?

试过,取得的文件名称全是?问号!
VB 打开包含Unicode UTF-8之类字符的文件名的文件

#7


不能正确显示是字体、语言的问题。
不要把不能显示和不能取到文件名混淆起来!

#8


引用 7 楼 Tiger_Zhao 的回复:
不能正确显示是字体、语言的问题。
不要把不能显示和不能取到文件名混淆起来!

也不是, 我就要求取得这个文件的fullpath,但你试试能不能朝这个文件写内容你就知道了(我没要求它显示。)

#9


引用 8 楼 Carlven2012 的回复:
Quote: 引用 7 楼 Tiger_Zhao 的回复:

不能正确显示是字体、语言的问题。
不要把不能显示和不能取到文件名混淆起来!

也不是, 我就要求取得这个文件的fullpath,但你试试能不能朝这个文件写内容你就知道了(我没要求它显示。)

VB 打开包含Unicode UTF-8之类字符的文件名的文件

#10


请举例。
那个字符?Unicode编码多少?

#11


引用 10 楼 Tiger_Zhao 的回复:
请举例。
那个字符?Unicode编码多少?

你新建个文本文档,用小键盘Alt + 0160为它命名(是个空白字符,我也不知道它的Unicode编码是多少),确定之后,这就是一个空白字符的文件名,你再用你的方法朝这个文件写内容试试。 反正我试过不行。

#12


啊,可以写入呀。 VB 打开包含Unicode UTF-8之类字符的文件名的文件

Option Explicit
Sub test()
    Dim FSO As New FileSystemObject, bFile As File
    For Each bFile In FSO.GetFolder("f:\").Files
        If bFile.Name Like "w?.txt" Then‘’这个文件名就是Alt+0160输入的
            bFile.OpenAsTextStream(ForWriting).Write "sdvssnvs"
            Debug.Print bFile.OpenAsTextStream(ForReading).ReadAll
        End If
    Next
End Sub



...

#13


引用 12 楼 Topc008 的回复:
啊,可以写入呀。 VB 打开包含Unicode UTF-8之类字符的文件名的文件

Option Explicit
Sub test()
    Dim FSO As New FileSystemObject, bFile As File
    For Each bFile In FSO.GetFolder("f:\").Files
        If bFile.Name Like "w?.txt" Then‘’这个文件名就是Alt+0160输入的
            bFile.OpenAsTextStream(ForWriting).Write "sdvssnvs"
            Debug.Print bFile.OpenAsTextStream(ForReading).ReadAll
        End If
    Next
End Sub




我主要是要写入二进制数据,用的是Open语句

            Dim c(1) As Byte
            c(0) = &HFF
            c(1) = &HD8

            Open bFile.Name For Binary As #1
            Put #1, , c
            Close #1

提示错误的文件名

#14


我把1.txt的文件名加了Alt + 0160。
全部要用Unicode版本的函数进行处理。
Option Explicit

Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileW" (ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileW" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long

Const MAX_PATH = 260
Const MAXDWORD = &HFFFF
Const INVALID_HANDLE_VALUE = -1
Const FILE_SHARE_READ = &H1
Const FILE_SHARE_WRITE = &H2
Const CREATE_NEW = 1
Const OPEN_EXISTING = 3
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000

Private Type FILETIME
    dwLowDateTime   As Long
    dwHighDateTime  As Long
End Type

Private Type WIN32_FIND_DATA
    dwFileAttributes            As Long
    ftCreationTime              As FILETIME
    ftLastAccessTime            As FILETIME
    ftLastWriteTime             As FILETIME
    nFileSizeHigh               As Long
    nFileSizeLow                As Long
    dwReserved0                 As Long
    dwReserved1                 As Long
    cFileName(MAX_PATH * 2 - 1) As Byte 'Unicode 需要双倍长度
    cAlternate(14 * 2 + 1)      As Byte 'Unicode 需要双倍长度
End Type

Const eeEndOfFile = 62                  'Input past end of file

Function StripNulls(bytes() As Byte) As String
    Dim i           As Long
    For i = 0 To UBound(bytes) Step 2
        If bytes(i) = 0 And bytes(i + 1) = 0 Then
            Exit For
        End If
    Next
    StripNulls = LeftB(bytes, i)
End Function

Function FindFiles(path As String, searchPattern As String) As Collection
    Dim oFiles      As Collection
    Dim hSearch     As Long
    Dim bFound      As Boolean
    Dim sFileName   As String
    Dim WFD         As WIN32_FIND_DATA

    Set oFiles = New Collection
    If Right(path, 1) <> "\" Then path = path & "\"

    sFileName = path & searchPattern
    hSearch = FindFirstFile(StrPtr(sFileName), WFD)
    If hSearch <> INVALID_HANDLE_VALUE Then
        bFound = True
        While bFound
            sFileName = StripNulls(WFD.cFileName)
            If (sFileName <> ".") And (sFileName <> "..") Then
                oFiles.Add path & sFileName
            End If
            bFound = FindNextFile(hSearch, WFD)
        Wend
        Call FindClose(hSearch)
    End If

    Set FindFiles = oFiles
End Function

Function ReadAllText(ByVal path As String) As String
    Dim hFile As Long
    Dim nSize As Long
    Dim bytes() As Byte
    Dim lBytesRead As Long
    
    hFile = CreateFile(StrPtr(path), GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)

    nSize = GetFileSize(hFile, 0)
    ReDim bytes(nSize - 1) As Byte
    ReadFile hFile, bytes(0), nSize, lBytesRead, ByVal 0&
    
    CloseHandle hFile
    
    If lBytesRead <> nSize Then Err.Raise eeEndOfFile

    ReadAllText = StrConv(bytes, vbUnicode)
End Function

Sub Main()
    Dim oFiles      As Collection
    Dim i           As Long
    Dim s           As String

    Set oFiles = FindFiles(App.path, "1*.*")
    For i = 1 To oFiles.Count
        s = oFiles(i)
        Debug.Print s, "U+" & Hex(AscW(Mid$(s, InStrRev(s, "\") + 2, 1)))
        Debug.Print ReadAllText(s)
    Next
End Sub

D:\temp\UnicodeFileName\1?.TXT U+A0
abc中文


又:不如改用VB.Net吧,一点问题都没有。
Imports System.Text
Imports System.IO

Module Module1

    Sub Main()
        For Each s As String In My.Computer.FileSystem.GetFiles("D:\temp\UnicodeFileName\", FileIO.SearchOption.SearchTopLevelOnly, "1*.*")
            Console.WriteLine(s)
            Console.WriteLine(BitConverter.ToString(Encoding.Unicode.GetBytes(Path.GetFileName(s))))
            Console.WriteLine(My.Computer.FileSystem.ReadAllText(s, Encoding.Default))
        Next
        Console.ReadLine()
    End Sub

End Module

#15


试试:文件名变量不要使用 String 类型。

在中文系统下,如果是字符串类型变量,不可识别的字符会统统被“自动”替换成?。

#16


引用 14 楼 Tiger_Zhao 的回复:
我把1.txt的文件名加了Alt + 0160。
全部要用Unicode版本的函数进行处理。
Option Explicit

Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileW" (ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileW" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long

Const MAX_PATH = 260
Const MAXDWORD = &HFFFF
Const INVALID_HANDLE_VALUE = -1
Const FILE_SHARE_READ = &H1
Const FILE_SHARE_WRITE = &H2
Const CREATE_NEW = 1
Const OPEN_EXISTING = 3
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000

Private Type FILETIME
    dwLowDateTime   As Long
    dwHighDateTime  As Long
End Type

Private Type WIN32_FIND_DATA
    dwFileAttributes            As Long
    ftCreationTime              As FILETIME
    ftLastAccessTime            As FILETIME
    ftLastWriteTime             As FILETIME
    nFileSizeHigh               As Long
    nFileSizeLow                As Long
    dwReserved0                 As Long
    dwReserved1                 As Long
    cFileName(MAX_PATH * 2 - 1) As Byte 'Unicode 需要双倍长度
    cAlternate(14 * 2 + 1)      As Byte 'Unicode 需要双倍长度
End Type

Const eeEndOfFile = 62                  'Input past end of file

Function StripNulls(bytes() As Byte) As String
    Dim i           As Long
    For i = 0 To UBound(bytes) Step 2
        If bytes(i) = 0 And bytes(i + 1) = 0 Then
            Exit For
        End If
    Next
    StripNulls = LeftB(bytes, i)
End Function

Function FindFiles(path As String, searchPattern As String) As Collection
    Dim oFiles      As Collection
    Dim hSearch     As Long
    Dim bFound      As Boolean
    Dim sFileName   As String
    Dim WFD         As WIN32_FIND_DATA

    Set oFiles = New Collection
    If Right(path, 1) <> "\" Then path = path & "\"

    sFileName = path & searchPattern
    hSearch = FindFirstFile(StrPtr(sFileName), WFD)
    If hSearch <> INVALID_HANDLE_VALUE Then
        bFound = True
        While bFound
            sFileName = StripNulls(WFD.cFileName)
            If (sFileName <> ".") And (sFileName <> "..") Then
                oFiles.Add path & sFileName
            End If
            bFound = FindNextFile(hSearch, WFD)
        Wend
        Call FindClose(hSearch)
    End If

    Set FindFiles = oFiles
End Function

Function ReadAllText(ByVal path As String) As String
    Dim hFile As Long
    Dim nSize As Long
    Dim bytes() As Byte
    Dim lBytesRead As Long
    
    hFile = CreateFile(StrPtr(path), GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)

    nSize = GetFileSize(hFile, 0)
    ReDim bytes(nSize - 1) As Byte
    ReadFile hFile, bytes(0), nSize, lBytesRead, ByVal 0&
    
    CloseHandle hFile
    
    If lBytesRead <> nSize Then Err.Raise eeEndOfFile

    ReadAllText = StrConv(bytes, vbUnicode)
End Function

Sub Main()
    Dim oFiles      As Collection
    Dim i           As Long
    Dim s           As String

    Set oFiles = FindFiles(App.path, "1*.*")
    For i = 1 To oFiles.Count
        s = oFiles(i)
        Debug.Print s, "U+" & Hex(AscW(Mid$(s, InStrRev(s, "\") + 2, 1)))
        Debug.Print ReadAllText(s)
    Next
End Sub

D:\temp\UnicodeFileName\1?.TXT U+A0
abc中文


又:不如改用VB.Net吧,一点问题都没有。
Imports System.Text
Imports System.IO

Module Module1

    Sub Main()
        For Each s As String In My.Computer.FileSystem.GetFiles("D:\temp\UnicodeFileName\", FileIO.SearchOption.SearchTopLevelOnly, "1*.*")
            Console.WriteLine(s)
            Console.WriteLine(BitConverter.ToString(Encoding.Unicode.GetBytes(Path.GetFileName(s))))
            Console.WriteLine(My.Computer.FileSystem.ReadAllText(s, Encoding.Default))
        Next
        Console.ReadLine()
    End Sub

End Module


嗯,试了下,用你的方法和writefile函数是可以了。看来应该是Open语句不支持Unicode字符文件名。

#17


而且用CreateFile、ReadFile、WriteFile函数的话,用fileName=Dir("E:\Test\*.*")找出来的文件名也同样可以读写

#18


错了,Dir命令获得的文件名不可以。另外发现一个有趣的现象:Word 2003打开这种特殊字符文件名的Doc文档时,能打开,但是会出现错误提示“内在不足...”,另外修改后无法保存。而且如果你新建一个doc文档,想保存为这种特殊文件名时,根本无法保存。看来Office 2003也没考虑到这种特殊文件名啊。

#19


特殊文件名多了去了!

#20


这两天暂时在整别的,还没有机会去试
再把问题详细说一下
这个文件系统显示一切正常,例如保存的网页
IE可以打开,记事本可以打开
但是其中有几个空格VB读取就变成了?
例如a=文件名
调试的时候会发现vb第一时间把中间的某几个字符换成?
然后才赋值。
最简单的测试是新建一个文本框,运行后,把这个空格粘帖进去就变成了?
当然遇到的不只是这个?
有些也就空格或者个别字符变成?
其它还能看出是哪个文件
有些完全不行。全是乱码
今天回去找网页试试。

#21


文本框是当前语言相关的,不支持全部的Unicode编码。
只有存放在String变量里的内容才不会自动替换(虽然Debug显示也有?,但是内容没变)。

#1


帮顶同问,因为这个问题我也遇到了,一直没能解决……(比如含Alt+0160弄出来的这种空白字符的文件名)

#2


操作系统保存文件名不是用UFT-8的吧,至少在WINDOWS中的NTFS中我记得应该是UTF-16,这个用FSO处理时应该支持。

#3


引用 2 楼 bcrun 的回复:
操作系统保存文件名不是用UFT-8的吧,至少在WINDOWS中的NTFS中我记得应该是UTF-16,这个用FSO处理时应该支持。

不行的,已经试过N多方法了,用FSO,Dir,FindFirstFile等API来获取该文件名都获取不了!

#4


求大神,这个应该有解决办法,不然VB岂不是废了,想写个文本处理器都只能放弃了

#5


用fso的for each file in fso.getfolder("path").files方法也不能获取吗?

#6


引用 5 楼 Topc008 的回复:
用fso的for each file in fso.getfolder("path").files方法也不能获取吗?

试过,取得的文件名称全是?问号!
VB 打开包含Unicode UTF-8之类字符的文件名的文件

#7


不能正确显示是字体、语言的问题。
不要把不能显示和不能取到文件名混淆起来!

#8


引用 7 楼 Tiger_Zhao 的回复:
不能正确显示是字体、语言的问题。
不要把不能显示和不能取到文件名混淆起来!

也不是, 我就要求取得这个文件的fullpath,但你试试能不能朝这个文件写内容你就知道了(我没要求它显示。)

#9


引用 8 楼 Carlven2012 的回复:
Quote: 引用 7 楼 Tiger_Zhao 的回复:

不能正确显示是字体、语言的问题。
不要把不能显示和不能取到文件名混淆起来!

也不是, 我就要求取得这个文件的fullpath,但你试试能不能朝这个文件写内容你就知道了(我没要求它显示。)

VB 打开包含Unicode UTF-8之类字符的文件名的文件

#10


请举例。
那个字符?Unicode编码多少?

#11


引用 10 楼 Tiger_Zhao 的回复:
请举例。
那个字符?Unicode编码多少?

你新建个文本文档,用小键盘Alt + 0160为它命名(是个空白字符,我也不知道它的Unicode编码是多少),确定之后,这就是一个空白字符的文件名,你再用你的方法朝这个文件写内容试试。 反正我试过不行。

#12


啊,可以写入呀。 VB 打开包含Unicode UTF-8之类字符的文件名的文件

Option Explicit
Sub test()
    Dim FSO As New FileSystemObject, bFile As File
    For Each bFile In FSO.GetFolder("f:\").Files
        If bFile.Name Like "w?.txt" Then‘’这个文件名就是Alt+0160输入的
            bFile.OpenAsTextStream(ForWriting).Write "sdvssnvs"
            Debug.Print bFile.OpenAsTextStream(ForReading).ReadAll
        End If
    Next
End Sub



...

#13


引用 12 楼 Topc008 的回复:
啊,可以写入呀。 VB 打开包含Unicode UTF-8之类字符的文件名的文件

Option Explicit
Sub test()
    Dim FSO As New FileSystemObject, bFile As File
    For Each bFile In FSO.GetFolder("f:\").Files
        If bFile.Name Like "w?.txt" Then‘’这个文件名就是Alt+0160输入的
            bFile.OpenAsTextStream(ForWriting).Write "sdvssnvs"
            Debug.Print bFile.OpenAsTextStream(ForReading).ReadAll
        End If
    Next
End Sub




我主要是要写入二进制数据,用的是Open语句

            Dim c(1) As Byte
            c(0) = &HFF
            c(1) = &HD8

            Open bFile.Name For Binary As #1
            Put #1, , c
            Close #1

提示错误的文件名

#14


我把1.txt的文件名加了Alt + 0160。
全部要用Unicode版本的函数进行处理。
Option Explicit

Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileW" (ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileW" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long

Const MAX_PATH = 260
Const MAXDWORD = &HFFFF
Const INVALID_HANDLE_VALUE = -1
Const FILE_SHARE_READ = &H1
Const FILE_SHARE_WRITE = &H2
Const CREATE_NEW = 1
Const OPEN_EXISTING = 3
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000

Private Type FILETIME
    dwLowDateTime   As Long
    dwHighDateTime  As Long
End Type

Private Type WIN32_FIND_DATA
    dwFileAttributes            As Long
    ftCreationTime              As FILETIME
    ftLastAccessTime            As FILETIME
    ftLastWriteTime             As FILETIME
    nFileSizeHigh               As Long
    nFileSizeLow                As Long
    dwReserved0                 As Long
    dwReserved1                 As Long
    cFileName(MAX_PATH * 2 - 1) As Byte 'Unicode 需要双倍长度
    cAlternate(14 * 2 + 1)      As Byte 'Unicode 需要双倍长度
End Type

Const eeEndOfFile = 62                  'Input past end of file

Function StripNulls(bytes() As Byte) As String
    Dim i           As Long
    For i = 0 To UBound(bytes) Step 2
        If bytes(i) = 0 And bytes(i + 1) = 0 Then
            Exit For
        End If
    Next
    StripNulls = LeftB(bytes, i)
End Function

Function FindFiles(path As String, searchPattern As String) As Collection
    Dim oFiles      As Collection
    Dim hSearch     As Long
    Dim bFound      As Boolean
    Dim sFileName   As String
    Dim WFD         As WIN32_FIND_DATA

    Set oFiles = New Collection
    If Right(path, 1) <> "\" Then path = path & "\"

    sFileName = path & searchPattern
    hSearch = FindFirstFile(StrPtr(sFileName), WFD)
    If hSearch <> INVALID_HANDLE_VALUE Then
        bFound = True
        While bFound
            sFileName = StripNulls(WFD.cFileName)
            If (sFileName <> ".") And (sFileName <> "..") Then
                oFiles.Add path & sFileName
            End If
            bFound = FindNextFile(hSearch, WFD)
        Wend
        Call FindClose(hSearch)
    End If

    Set FindFiles = oFiles
End Function

Function ReadAllText(ByVal path As String) As String
    Dim hFile As Long
    Dim nSize As Long
    Dim bytes() As Byte
    Dim lBytesRead As Long
    
    hFile = CreateFile(StrPtr(path), GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)

    nSize = GetFileSize(hFile, 0)
    ReDim bytes(nSize - 1) As Byte
    ReadFile hFile, bytes(0), nSize, lBytesRead, ByVal 0&
    
    CloseHandle hFile
    
    If lBytesRead <> nSize Then Err.Raise eeEndOfFile

    ReadAllText = StrConv(bytes, vbUnicode)
End Function

Sub Main()
    Dim oFiles      As Collection
    Dim i           As Long
    Dim s           As String

    Set oFiles = FindFiles(App.path, "1*.*")
    For i = 1 To oFiles.Count
        s = oFiles(i)
        Debug.Print s, "U+" & Hex(AscW(Mid$(s, InStrRev(s, "\") + 2, 1)))
        Debug.Print ReadAllText(s)
    Next
End Sub

D:\temp\UnicodeFileName\1?.TXT U+A0
abc中文


又:不如改用VB.Net吧,一点问题都没有。
Imports System.Text
Imports System.IO

Module Module1

    Sub Main()
        For Each s As String In My.Computer.FileSystem.GetFiles("D:\temp\UnicodeFileName\", FileIO.SearchOption.SearchTopLevelOnly, "1*.*")
            Console.WriteLine(s)
            Console.WriteLine(BitConverter.ToString(Encoding.Unicode.GetBytes(Path.GetFileName(s))))
            Console.WriteLine(My.Computer.FileSystem.ReadAllText(s, Encoding.Default))
        Next
        Console.ReadLine()
    End Sub

End Module

#15


试试:文件名变量不要使用 String 类型。

在中文系统下,如果是字符串类型变量,不可识别的字符会统统被“自动”替换成?。

#16


引用 14 楼 Tiger_Zhao 的回复:
我把1.txt的文件名加了Alt + 0160。
全部要用Unicode版本的函数进行处理。
Option Explicit

Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileW" (ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileW" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long

Const MAX_PATH = 260
Const MAXDWORD = &HFFFF
Const INVALID_HANDLE_VALUE = -1
Const FILE_SHARE_READ = &H1
Const FILE_SHARE_WRITE = &H2
Const CREATE_NEW = 1
Const OPEN_EXISTING = 3
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000

Private Type FILETIME
    dwLowDateTime   As Long
    dwHighDateTime  As Long
End Type

Private Type WIN32_FIND_DATA
    dwFileAttributes            As Long
    ftCreationTime              As FILETIME
    ftLastAccessTime            As FILETIME
    ftLastWriteTime             As FILETIME
    nFileSizeHigh               As Long
    nFileSizeLow                As Long
    dwReserved0                 As Long
    dwReserved1                 As Long
    cFileName(MAX_PATH * 2 - 1) As Byte 'Unicode 需要双倍长度
    cAlternate(14 * 2 + 1)      As Byte 'Unicode 需要双倍长度
End Type

Const eeEndOfFile = 62                  'Input past end of file

Function StripNulls(bytes() As Byte) As String
    Dim i           As Long
    For i = 0 To UBound(bytes) Step 2
        If bytes(i) = 0 And bytes(i + 1) = 0 Then
            Exit For
        End If
    Next
    StripNulls = LeftB(bytes, i)
End Function

Function FindFiles(path As String, searchPattern As String) As Collection
    Dim oFiles      As Collection
    Dim hSearch     As Long
    Dim bFound      As Boolean
    Dim sFileName   As String
    Dim WFD         As WIN32_FIND_DATA

    Set oFiles = New Collection
    If Right(path, 1) <> "\" Then path = path & "\"

    sFileName = path & searchPattern
    hSearch = FindFirstFile(StrPtr(sFileName), WFD)
    If hSearch <> INVALID_HANDLE_VALUE Then
        bFound = True
        While bFound
            sFileName = StripNulls(WFD.cFileName)
            If (sFileName <> ".") And (sFileName <> "..") Then
                oFiles.Add path & sFileName
            End If
            bFound = FindNextFile(hSearch, WFD)
        Wend
        Call FindClose(hSearch)
    End If

    Set FindFiles = oFiles
End Function

Function ReadAllText(ByVal path As String) As String
    Dim hFile As Long
    Dim nSize As Long
    Dim bytes() As Byte
    Dim lBytesRead As Long
    
    hFile = CreateFile(StrPtr(path), GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)

    nSize = GetFileSize(hFile, 0)
    ReDim bytes(nSize - 1) As Byte
    ReadFile hFile, bytes(0), nSize, lBytesRead, ByVal 0&
    
    CloseHandle hFile
    
    If lBytesRead <> nSize Then Err.Raise eeEndOfFile

    ReadAllText = StrConv(bytes, vbUnicode)
End Function

Sub Main()
    Dim oFiles      As Collection
    Dim i           As Long
    Dim s           As String

    Set oFiles = FindFiles(App.path, "1*.*")
    For i = 1 To oFiles.Count
        s = oFiles(i)
        Debug.Print s, "U+" & Hex(AscW(Mid$(s, InStrRev(s, "\") + 2, 1)))
        Debug.Print ReadAllText(s)
    Next
End Sub

D:\temp\UnicodeFileName\1?.TXT U+A0
abc中文


又:不如改用VB.Net吧,一点问题都没有。
Imports System.Text
Imports System.IO

Module Module1

    Sub Main()
        For Each s As String In My.Computer.FileSystem.GetFiles("D:\temp\UnicodeFileName\", FileIO.SearchOption.SearchTopLevelOnly, "1*.*")
            Console.WriteLine(s)
            Console.WriteLine(BitConverter.ToString(Encoding.Unicode.GetBytes(Path.GetFileName(s))))
            Console.WriteLine(My.Computer.FileSystem.ReadAllText(s, Encoding.Default))
        Next
        Console.ReadLine()
    End Sub

End Module


嗯,试了下,用你的方法和writefile函数是可以了。看来应该是Open语句不支持Unicode字符文件名。

#17


而且用CreateFile、ReadFile、WriteFile函数的话,用fileName=Dir("E:\Test\*.*")找出来的文件名也同样可以读写

#18


错了,Dir命令获得的文件名不可以。另外发现一个有趣的现象:Word 2003打开这种特殊字符文件名的Doc文档时,能打开,但是会出现错误提示“内在不足...”,另外修改后无法保存。而且如果你新建一个doc文档,想保存为这种特殊文件名时,根本无法保存。看来Office 2003也没考虑到这种特殊文件名啊。

#19


特殊文件名多了去了!

#20


这两天暂时在整别的,还没有机会去试
再把问题详细说一下
这个文件系统显示一切正常,例如保存的网页
IE可以打开,记事本可以打开
但是其中有几个空格VB读取就变成了?
例如a=文件名
调试的时候会发现vb第一时间把中间的某几个字符换成?
然后才赋值。
最简单的测试是新建一个文本框,运行后,把这个空格粘帖进去就变成了?
当然遇到的不只是这个?
有些也就空格或者个别字符变成?
其它还能看出是哪个文件
有些完全不行。全是乱码
今天回去找网页试试。

#21


文本框是当前语言相关的,不支持全部的Unicode编码。
只有存放在String变量里的内容才不会自动替换(虽然Debug显示也有?,但是内容没变)。