Reputation: 659
I would like to programmatically rename a Windows folder containing one or more illegal Windows characters using VBA. (The folder tree comes from a Mac/Linux environment).
The illegal character shows as a .
in Windows/File Explorer, which can manipulate the folder OK.
Methods I have tried unsuccessfully include using the FileSystem
Object and the VBA Name
command.
Getting the actual name of the folder with the illegal char is the problem.
Doing dir /X
shows the short (8.3 format) name of the folder but the full name appears in the command window with ?
replacing the illegal char.
Supplying this name with the ? to various routines for folder operations (e.g. FileSystemObject
GetFolder
, VB Name
function, GetShortName
) results in the operation not finding the file to operate on.
Running Dir /X > Foldernames.txt
produces an ASCII file which shows a ? replacing the illegal char, but examining the file in a binary editor (Frhed
) shows 3 bytes for the illegal character in the full folder name. These are Chr(239)
, Chr(128)
and Chr(162)
. Using this string to replace the illegal char the folder name still results in folder not found behaviour.
Using the actual illegal value (Chr (149)
)in the folder name also results in folder not found behaviour.
A short file name (8.3 format ) is shown in the Dir /X
output and the folder can be accessed via this name. However, I can't see how to distinguish between short names for folders with different illegal chars in the same position, and shortname generation only occurs when the folder exists, so unless I can access the foldername with the illegal char directly it's not much help.
There is a unicode symbol for a placeholder (UxFFFD
) which shows as a question mark in a black diamond which I have seen occasionally but not in in any Dir /X
listings.
Upvotes: 0
Views: 532
Reputation: 659
I have found a very laborious way of accessing files and folders with illegal characters in them via their short (8.3) format names. Generating a short name from a path which does not exist is not possible - as the algorithm for generating short names is apparently subject to change and I could not find a reverse-engineer of it.
The only way to get the short name is to do a Dir /X listing for the folder with illegal char or containing a file name with an illegal char and send it to file, which can then be parsed. The long name of the file or folder in the file sometimes includes the illegal character (if it is a ?, which is commonest) so that can be searched for. VBA code for dealing with folders is below. Once the short name of the folder with the illegal char is found it can be easily renamed and returned as a ByRef parameter.
' Procedure : LegalPath
' Author : Simon
' Date : 11/12/2017
' Purpose : returns true if all path chars are legal and changes illegals to _ in sLegalPath if not
Public Function LegalPath(ByVal sPath As String, ByRef sLegalPath As String) As Boolean
Dim iColon As Long
Dim J As Long
Dim sIllegalChars As String
Dim sOutPath As String
On Error GoTo LegalPath_Error
LegalPath = False
sIllegalChars = ""
'If InStr(sPath, "\") <> 0 Then Exit Function
If InStr(sPath, "/") <> 0 Then sIllegalChars = sIllegalChars + "/"
iColon = InStr(sPath, ":") ' allow colon at loc 2
If iColon <> 0 And iColon <> 2 Then sIllegalChars = sIllegalChars + ":"
If InStr(sPath, "*") <> 0 Then sIllegalChars = sIllegalChars + "*"
If InStr(sPath, "?") <> 0 Then sIllegalChars = sIllegalChars + "?"
If InStr(sPath, "<") <> 0 Then sIllegalChars = sIllegalChars + "<"
If InStr(sPath, ">") <> 0 Then sIllegalChars = sIllegalChars + ">"
If InStr(sPath, "|") <> 0 Then sIllegalChars = sIllegalChars + "|"
' check for bullet code (149)
For J = 1 To Len(sPath)
If (asc(Mid(sPath, J, 1)) = 149) Then
sIllegalChars = sIllegalChars & Mid(sPath, J, 1)
End If
Next J
If (sIllegalChars <> "") Then
LegalPath = False
' replace with illegals with underscore
sOutPath = sLegalPath
For J = 1 To Len(sIllegalChars)
sOutPath = Replace(sOutPath, Mid(sIllegalChars, J, 1), "_")
Next J
sLegalPath = sOutPath
Dim sParentFolder As String
sParentFolder = GetFolderFromPath(TruncateR(sPath))
Call shell(GetDAFolder & "\ListDir.bat """ & sParentFolder & """ """ & GetDAFolder & "\ListDir.txt""")
Dim a As TextStream
Dim fs As New Scripting.FileSystemObject
Set a = fs.OpenTextFile(GetDAFolder & "\ListDir.txt", ForReading, False)
Dim sWindowsFolderName As String
Dim sWindowsShortName As String
Dim sLine As String
sWindowsFolderName = GetFileFromPath(TruncateR(sPath))
Do While a.AtEndOfStream <> True
sLine = a.ReadLine
If (Len(sLine) > 50) Then
If (Mid(sLine, 50) = sWindowsFolderName) Then
sWindowsShortName = Mid(sLine, 37, 8)
Name sParentFolder & "\" & sWindowsShortName As sLegalPath
Exit Function
End If
End If
Loop
Else
LegalPath = True
End If
End Function
For files with illegal names, the approach is very similar. Illegal chars are replaced by _. Note that Unicode characters are not flagged as illegal.
Public Function IsIllegalFileCharIN(ByRef sFileName As String, ByVal sFolder As String) As Boolean
Dim sRegex As String
Dim objRegExp As New RegExp
Dim sOut As String
sRegex = "[<>:""/\\|?*„]+"
With objRegExp
.Pattern = sRegex
.IgnoreCase = True
.Global = True
IsIllegalFileCharIN = .test(sFileName)
End With
If (IsIllegalFileCharIN) Then
Dim sLegalFileName As String
With objRegExp
.Pattern = sRegex
.IgnoreCase = True
.Global = True
sLegalFileName = .Replace(sFileName, "_")
End With
' find file short name
Call shell(GetDAFolder & "\ListDir.bat """ & sFolder & """ """ & GetDAFolder & "\ListDir.txt""")
Dim a As TextStream
Dim fs As New Scripting.FileSystemObject
Set a = fs.OpenTextFile(GetDAFolder & "\ListDir.txt", ForReading, False)
Dim sWindowsFolderName As String
Dim sWindowsShortName As String
Dim sLine As String
sWindowsFolderName = sFileName
Do While a.AtEndOfStream <> True
sLine = a.ReadLine
If (Len(sLine) > 50) Then
If (Mid(sLine, 50) = sWindowsFolderName) Then
sWindowsShortName = Mid(sLine, 37, 12)
Name sFolder & "\" & sWindowsShortName As sFolder & "\" & sLegalFileName
sFileName = sLegalFileName
Exit Function
End If
End If
Loop
End If
On Error GoTo 0
End Function
GetDAFolder is a dedicated folder. The batch file ListDir.Bat contains
dir /X %1 >%2
Upvotes: 0
Reputation: 304
I suggest doing this via a batch-job.
Get the actual name of the file via a list.
You could use a batch like this:
dir "F:\batchs" > "F:\batchs\list.txt"
exit
If you found out the name copy it and replace "'inserthere'" in this code:
ren '_inserthere_' newname.txt
exit
and then run this as a batch job again. This might do the job.
Upvotes: 0