Delphi 7, TFileStream cant open files with special characters

2.5k views Asked by At

This line:

TFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);

drops an exception if the filename contain something like ñ

4

There are 4 answers

6
David Heffernan On BEST ANSWER

You are, ultimately calling CreateFileA, the ANSI API, and the characters you use have no ANSI encoding. The only way to get beyond this is to open the file with CreateFileW, the Unicode API.

You might not realise that you call CreateFileA, but that's how the Delphi 7 file stream is implemented.

One easy way to solve your problems is to upgrade to the latest Delphi which has good support for the native Windows Unicode API.

If you are stuck with ANSI Delphi then you still need to call CreateFileW. You can do this to create a file handle. You'll need to pass a UTF-16 string to that API. Use WideString to store it. You'll also need to get the filename from the user in UTF-16 form. Which means a call to GetOpenFileNameW or IFileDialog. Create a stream by passing the file handle to THandleStream.

To make all this possible you would use the TNT Unicode libraries. They work well but will impose a big port on you.

Frankly, the right way forward is to use modern tools that support Unicode.

12
Hans On

Best solution is to go Unicode, but if that is not an option, you can still solve the problem.

In Windows you can set what codepage to use for non-Unicode programs. Just change it to support the correct language (Spanish?). Then the code should work.

  • Windows 7: Control Panel > Region and Language > Administrative > Language for non-Unicode programs

  • Windows XP: Control Panel > Regional and Language > Advanced > Language for non-Unicode programs

1
MDenis On

You can use the TntUnicode units to have UTF8 support under Delphi 7. Add TntClasses to your Uses and make the call like this:

TTntFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);

Make sure that fileName is widestring.

Here you can get a copy of TntUnicode: https://github.com/rofl0r/TntUnicode

0
Deltics On

UTF16 can be thought of as a codepage, just like all of the possible ANSI codepages.

As Remy mentions in his comment, assuming your ANSI codepage supports the required characters in your Unicode string you simply have to convert that Unicode version of that string to the equivalent ANSI codepage version.

The Delphi compiler can take care of a simple conversion for you automatically, which you use simply by casting a WIDEString (UTF16) to an (ANSI)String:

const
  WIDE_FILENAME : WIDEString = 'fuññy.txt';
var
  sFilename: String;
  strm: TFileStream;
begin
  sFilename := String(WIDE_FILENAME);

  strm := TFileStream.Create(sFilename, fmOpenRead);
  // etc
end;      

This works perfectly well even on (e.g.) Delphi 7. The only caveat is that the codepage involved (the system default) must support the extended characters in the Unicode string.

NOTE: The above code uses the String type rather than ANSIString explicitly. On Delphi versions where String is ANSIString, this has the required effect but also is portable to versions where String is UnicodeString (should you upgrade your version later).

If you use ANSIString explicitly in this case, the result will be a double conversion if/when you upgrade:

// Unicode compiler using ANSIString type....
var
  sFilename: ANSIString;
begin
  sFilename := ANSIString(WIDE_FILENAME);            // Codepage conversion from UTF16 to ANSI
  strm := TFileStream.Create(sFilename, fmOpenRead); // Will implicitly convert *back* from ANSI to WIDE

versus

// Unicode compiler using String type....
var
  sFilename: String;
begin
  sFilename := String(WIDE_FILENAME);                // String type conversion from WideString to UnicodeString
  strm := TFileStream.Create(sFilename, fmOpenRead); // No further conversion necessary