How can I create text files with special characters in their filenames
Asked Answered
F

2

2

Demonstration of my problem

  • Open a new Excel workbook and save these symbols 設計師協會 to cell [A1]
  • insert the following VBA code somewhere in the editor (Alt+F11)
  • execute it line per line (F8)
Sub test()

    strCRLF = StrConv(vbCrLf, vbUnicode)
    strSpecialchars = StrConv(Cells(1, 1), vbUnicode)
    strFilename = "c:\test.txt"

    Open strFilename For Output As #1
    Print #1, strSpecialchars & strCRLF;
    Close #1

End Sub

You will get a textfile which contains the chinese characters from [A1]. This proofs that VBA is able to handle unicode characters if you know the trick with adding StrConv(vbCrLf, vbUnicode)

Now try the same for strFilename = "C:\" & strSpecialchars & ".txt". You will get an error that you can't create a file with this filename. Of course you can't use the same trick adding a new line since its a filename.

How can I create text files with special characters in their filenames using VBA?
Is there a work-around or am I doing something wrong?

Note

Forlorn answered 10/1, 2013 at 15:42 Comment(1)
You don't absolutely have to set a reference (although I don't know why that's such a problem). You can use late binding without a reference :- Dim FSO as Object {newline} Set FSO = CreateObject("Scripting.FileSystemObject") - but you lose out on all the Intellisense and predefined constants. Are you sure this method would help though ? I still get a bad filename error.Brevier
D
7

Value retrieved from the cell is already in Unicode.

StrConv(vbUnicode) gives you "double unicode" which is broken because it went through a conversion using the current system codepage.
Then the Print command converts it back to "single unicode", again using the current system codepage. Don't do this. You're not saving unicode, you're saving invalid something that may only appear valid on your particular computer under your current settings.

If you want to output Unicode data (that is, avoid the default VB mechanism of auto-converting output text from Unicode to ANSI), you have several options.

The easiest is using FileSystemObject without trying to invent anything about unicode conversions:

With CreateObject("Scripting.FileSystemObject")
  With .CreateTextFile("C:\" & Cells(1).Value & ".txt", , True)
    .Write Cells(1).Value
    .Close
  End With
End With

Note the last parameter that controls Unicode.

If you don't want that, you can declare CreateFileW and WriteFile functions:

Private Declare Function CreateFileW Lib "kernel32.dll" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByRef lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function WriteFile Lib "kernel32.dll" (ByVal hFile As Long, ByRef lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, ByRef lpNumberOfBytesWritten As Long, ByRef lpOverlapped As Any) As Long

Private Const CREATE_ALWAYS As Long = 2
Private Const GENERIC_WRITE As Long = &H40000000
Dim hFile As Long
hFile = CreateFileW(StrPtr("C:\" & Cells(1).Value & ".txt"), GENERIC_WRITE, 0, ByVal 0&, CREATE_ALWAYS, 0, 0)

Dim val As String
val = Cells(1).Value

WriteFile hFile, &HFEFF, 2, 0, ByVal 0&  'Unicode byte order mark (not required, but to please Notepad)
WriteFile hFile, ByVal StrPtr(val), Len(val) * 2, 0, ByVal 0&

CloseHandle hFile
Disassembly answered 12/1, 2013 at 11:24 Comment(3)
+1 - Both methods worked fot me. I just want to mention that there is a third valid method using CreateObject("ADODB.Stream"), which has worked for me.Forlorn
@user202729 The legal use of StrConv with vbUnicode/vbFromUnicode is when you are converting from an ANSI byte array to a UTF16 string with vbUnicode or from a UTF16 string to an ANSI byte array with vbFromUnicode. Passing an already-string to vbUnicode is rubbish.Disassembly
Alright I got it (related question) (sorry that it's supposedly "basic knowledge", but I can't find it anywhere in the documentation. It's still confusing that VBA documentation appears to use "Unicode" to mean "UCS-2", and how functions like LeftB or StrConv returns a String instead of a Byte(), however.Compliant
R
4

You are on the right track with the FileSystemObject. As Morbo mentioned you can late bind this so no reference is set. The FSO has a CreateTextFile function which can be set in unicode so the characters will appear as '??????' in VBA but will write correctly to the filename. Note the second parameter of the CreateTextFile function specifies a unicode string for the filename. The following will do the trick for you:

Sub test()
    Dim strCRLF As String, strSpecialchars As String, strFilename As String
    Dim oFSO As Object, oFile As Object

    strCRLF = StrConv(vbCrLf, vbUnicode)
    strSpecialchars = StrConv(Cells(1, 1), vbUnicode)
    strFilename = "C:\" & Cells(1, 1).Value & ".txt"

    Set oFSO = CreateObject("Scripting.FileSystemObject")
    Set oFile = oFSO.CreateTextFile(strFilename, , True)

    oFile.Write strSpecialchars & strCRLF

    oFile.Close

    Set oFile = Nothing
    Set oFSO = Nothing
End Sub
Recover answered 12/1, 2013 at 10:53 Comment(1)
Setting CreateTextFile(strFile,,Unicode=True) works to enable writing unicode characters to file.Tester

© 2022 - 2024 — McMap. All rights reserved.