Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What are the problem?

posted at

updated at

Delphi のソースコードを一括で UTF-8 に変換する

はじめに

Delphi のソースファイルはデフォルト ANSI コードページが使われるので、海外の方が作ったアプリのソースファイルを開くとソースコードが破壊される事があります。

また、自身で書いたコードもデフォルトでは Shift_JIS (コードページ: 932) で保存されている事になります (日本語環境をお使いの場合)。

Delphi 2005 以降 (正確には Delphi 8 以降)、ソースファイルを UTF-8 で保存する事が可能です。
image.png
古い IDE とファイルを共有するのでもない限り、エンコーディングを Shift_JIS にする必然性はありません。

デフォルトのファイルエンコード

デフォルトのファイルエンコードを UTF-8 に変更するにはいくつか方法があります。

1. Delphi 10.4 Sydney 以降

[ツール | オプション...] でオプションダイアログを開き [ユーザーインターフェイス | エディタ] でデフォルトのファイルエンコードを指定する事ができます。
image.png

2. UTF8ize Plugin (Delphi 2010 ~ 10.3 Rio)

UTF8ize Plugin を導入すると、新規に作成したり開いたユニットのファイルエンコードが UTF-8 にセットされます。

先述の通り、10.4 Sydney 以降ではオプションダイアログでデフォルトのファイルエンコードを変更できますので、UTF8ize が必要になるのは Delphi 2010 ~ 10.3 Rio という事になるかと思います。

3. レジストリ

Delphi 8 以降のガリレオ IDE では、レジストリを変更する事によって、デフォルトのファイルエンコードを UTF-8 に変更できます。

Delphi BDS バージョン レジストリキー
Delphi 8, 2005, 2006, 2007 2.0, 3.0, 4.0, 5.0 HKEY_CURRENT_USER\Software\Borland
Delphi 2009, 2010 6.0, 7.0 HKEY_CURRENT_USER\Software\CodeGear
Delphi XE 以降 8.0 ~ HKEY_CURRENT_USER\Software\Embarcadero

いずれかの、BDS\xx.x\Editor (xx.x は BDS バージョン) にある (なければ新規作成) DefaultFileFilter (REG_SZ) の値を Borland.FileFilter.ANSI から Borland.FileFilter.UTF8ToUTF8 に変更します。

例えば Delphi 2007 の場合、HKEY_CURRENT_USER\Software\Borland\BDS\5.0\EditorDefaultFileFilter を新規作成してデータを Borland.FileFilter.UTF8ToUTF8 に設定します 1
image.png
See also:

ANSI2UTF8

Delphi のソースコードのコードページを一括で UTF-8 に変換するツールを Delphi で書いてみます。

ソースコード

Delphi 10.3 Rio 以降でコンパイルできますが、インライン変数宣言を使わなければ Delphi XE とかでもコンパイルできると思います。

ANSI2UTF8.dpr
program ANSI2UTF8;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.Classes, System.IOUtils, System.StrUtils, System.WideStrUtils;

const
  SearchOption: array [Boolean] of TSearchOption =
    (TSearchOption.soTopDirectoryOnly, TSearchOption.soAllDirectories);

begin
  var Dir := '';
  var Enc: TEncoding := nil;
  var ErrFlg := False;
  var IsBackup := False;
  var IsRecursive := False;

  Writeln('ANSI to UTF-8 File Converter - Copyright (c) 2021 DEKO');
  repeat
    if ParamCount = 0 then
      begin
        ErrFlg := True;
        Break;
      end;
    IsBackup    := FindCmdLineSwitch('b', True);
    IsRecursive := FindCmdLineSwitch('r', True);
    var sCodePage := '';
    var IsCodePage  := FindCmdLineSwitch('c', sCodePage);
    if IsCodePage then
      begin
        var CodePage := StrToIntDef(sCodePage, -1);
        if CodePage = -1 then
          begin
            Writeln('Invalid Codepage.');
            ErrFlg := True;
            Break;
          end
        else
          Enc := TEncoding.GetEncoding(CodePage);
      end;
    Dir := ExpandFileName(ParamStr(ParamCount));
    if not TDirectory.Exists(Dir) then
      begin
        Writeln('Invalid Path.');
        ErrFlg := True;
        Break;
      end;
  until True;

  if ErrFlg then
    begin
      Writeln(#$0D#$0A'Usage: ' + TPath.GetFileNameWithoutExtension(ParamStr(0)) + ' [-B] [-C CodePage] [-R] ProjectDir');
      Writeln('');
      Writeln('  -B'#$09#$09'Create Backup File');
      Writeln('  -C CodePage'#$09'Set CodePage');
      Writeln('  -R'#$09#$09'Recursive');
      ExitCode := -1;
      Exit;
    end;

  ExitCode := 1;

  if Enc = nil then
    Enc := TEncoding.GetEncoding(0);

  var MS := TMemoryStream.Create;
  var SL := TStringList.Create;
  try
    Writeln('');
    for var FileName in TDirectory.GetFiles(Dir, '*.*', SearchOption[IsRecursive]) do
      begin
        var Ext := LowerCase(TPath.GetExtension(FileName));
        if not MatchStr(Ext, ['.pas', '.inc', '.dpr', '.dpk']) then
          Continue;
        MS.LoadFromFile(FileName);
        if not HasUTF8BOM(MS) then
          begin
            MS.Position := 0;
            SL.LoadFromStream(MS, Enc);
            SL.SaveToFile(FileName, TEncoding.UTF8);
            if IsBackup then
              begin
                MS.Position := 0;
                MS.SaveToFile(FileName + '.bak');
              end;
            Writeln(FileName);
          end;
      end;
  finally
    SL.Free;
    MS.Free;
    Enc.Free;
  end;

  ExitCode := 0;
end.

C++ Builder にも対応させたい場合には、MatchStr() の所に、変換対象となるソースファイルの拡張子を追加してください。

ANSI2UTF8.dpr
        if not MatchStr(Ext, ['.pas', '.inc', '.dpr', '.dpk',
                              '.c'  , '.h'  , '.cpp', '.hpp']) then
          Continue;

何か漏れているような...?

使い方

ANSI2UTF8.EXE にプロジェクトフォルダを指定して実行します。

ANSI to UTF-8 File Converter - Copyright (c) 2021 DEKO

Usage: ANSI2UTF8 [-B] [-C CodePage] [-R] ProjectDir

  -B            Create Backup File
  -C CodePage   Set CodePage
  -R            Recursive
フラグ 意味
-B 変換前のファイルを .BAK として残します
-C CodePage 変換前のファイルのコードページを指定します
-R サブフォルダ内のファイルも変換します

普通の使い方は、

ANSI2UTF8 <ProjectDir>

です。

サブフォルダにもソースファイルがある場合には -R を、海外の方が作られたプロジェクトファイルを日本語環境で変換する場合には -C 1252 等を指定してください 2

See also:

おわりに

このツールは未公開だと思って Qiita に記事を書いたのですが、やっぱり公開してました。がっでむ。

折角なので、整理したソースコードを反映させておきました。


  1. Qiita の記事からコピペすると末尾にスペースが含まれがちなので注意してください。 

  2. 何語で書かれているのかを調べ、適切なコードページを指定する必要があります。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
1
Help us understand the problem. What are the problem?