AX

電霊ログ

私のやってるサイト、電子の言霊の活動ログ

カテゴリークラウド

タグリスト

最近のトラックバック

月別アーカイブ(タブ)

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  • [HSP3] Unicodeでファイルを開く

前にHSP本家の掲示板で、ファイル名に中国語を含んだファイルを開く事についての質問があり、Unicode を使えば良いんじゃないかって話のやり取りを見て、自分なりに調べて作ってたスクリプトが有ったんですが、質問者が未解決のまま居なくなってしまって、そのまま投稿する機会を失っていました。せっかく書いたのに公開出来ないのも寂しいので、こちらで公開してみます。(いくらか適当なトコもあったりしますが…)


追記(2014/08/20):
久しぶりにスクリプトを見返したら、あまりにもいい加減すぎたので修正しました。

追記(2015/01/18):
ユニコードを手軽に扱う HSP3 用モジュール mod_unicode をサイトにて公開しました。この記事の内容がよく分からなかったり、自分でやるのが面倒くさい人は使ってみてください。


・指定したテキストファイルのファイル名とその内容の一部をUnicodeとして表示
#スクリプトは HSP3.4rc1 で実行確認しています

#include "comdlg32.as"
#include "user32.as"
#include "kernel32.as"
#include "advapi32.as"

#define MAX_PATH                        $00000104
#define OFN_FILEMUSTEXIST               $00001000
#define OFN_ALLOWMULTISELECT            $00000200
#define OFN_EXPLORER                    $00080000
#define OPENFILENAME_SIZE_VERSION_400   $0000004C
#define DT_WORDBREAK                    $00000010
#define DT_EXPANDTABS                   $00000040
#define GENERIC_READ                    $80000000
#define OPEN_EXISTING                   $00000003
#define FILE_ATTRIBUTE_NORMAL           $00000080
#define INVALID_HANDLE_VALUE            $FFFFFFFF
#define FILE_SHARE_READ                 $00000001
#define CP_ACP                          $00000000
#define MB_PRECOMPOSED                  $00000001


// OPENFILENAME構造体初期化に必要なものを準備

    // フィルター文字列が Unicode で入るだけの大きさを確保
    sdim filter, 512
    // フィルター文字列に置換マーク(0)を埋め込んで Unicode に変換(下で手動で \0 を置換)
    cnvstow filter, "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"           ; HSP に \0 は無いので実際は数字の 0 になる
    // マークを Unicode のヌル文字(2byte の 0x0000)に置換(上書き)
    wpoke filter, 22 :wpoke filter, 34 :wpoke filter, 64 :lpoke filter, 72  ; 終端は並んでるので 4byte 一気に換えてみた
    // ダイアログの選択結果を格納する変数を作成
    sdim filepath, MAX_PATH * 2  ;ここにファイルのパスが入る
    sdim filename, MAX_PATH * 2  ;ここにファイルネームが入る
    cnvstow caption, "開く"

// OPENFILENAME構造体初期化

    // 構造体用の変数を作って適切な値を設定する
#if 01 ; Windows 2000以降ならこっちのサイズを指定するみたい
    dim ofn, 22
    ofn(0) = 88
#else
    dim ofn, OPENFILENAME_SIZE_VERSION_400 / 4
    ofn(0) = OPENFILENAME_SIZE_VERSION_400
#endif
    ofn(1) = hwnd
    ofn(3) = varptr(filter)
    ofn(7) = varptr(filepath), MAX_PATH     ; Unicode 版ではこの MAX_PATH で指定した
    ofn(9) = varptr(filename), MAX_PATH     ; メンバは文字数扱いらしい
    ofn(12) = varptr(caption)
    ofn(13) = OFN_FILEMUSTEXIST

    // ファイルオープンダイアログを表示
    GetOpenFileNameW varptr(ofn)
    if stat = 0 :mes "ダイアログキャンセル" :stop

// 選択したファイルを読み込む

    // 取得したファイルネームを使ってファイルを開きそのハンドルを得る
    CreateFileW varptr(filename), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
    hfile = stat
    if hfile = INVALID_HANDLE_VALUE { mes "CreateFileWでエラー" : stop }

    // テキストファイルの内容を変数に読み込む
    GetFileSizeEx hfile, varptr(filesize)                           ; ファイルのハンドルからファイルサイズ取得
    sdim text, filesize + 2                                         ; ファイルの内容を入れる変数を作成(ヌル文字分加算)
    ReadFile hfile, varptr(text), filesize, varptr(readsize), 0     ; ファイルの内容を取得
    CloseHandle hfile                                               ; ハンドルを閉じる

// 読み込んだファイルを表示

    // ファイルのパスを表示
    font MSGOTHIC16
    mes "ファイルパス:"
    rect = 0GINFO_CY640GINFO_CY + 16 : pos , rect(3)
    DrawTextW hdcvarptr(filepath), -1varptr(rect), DT_WORDBREAK|DT_EXPANDTABS

    // ファイルの名前を表示
    mes "\nファイルネーム:"
    rect = 0GINFO_CY640GINFO_CY + 16 : pos , rect(3)
    DrawTextW hdcvarptr(filename), -1varptr(rect), DT_WORDBREAK|DT_EXPANDTABS

    // ファイルの内容が Unicode か判定し Unicode に合わせる
    IsTextUnicode varptr(text), filesize, 0
    if stat {   /* Unicode ならば */
        // 文字数取得
        lstrlenW varptr(text)       ; ここで取得するのは
        unilen = stat               ; 文字サイズでなく文字数(ヌル文字は含まれない)
        mes "\nファイルの内容 (Unicode) : "
    } else {    /* Shift_JIS ならば */
        // 文字数取得
        MultiByteToWideChar CP_ACP, MB_PRECOMPOSED, varptr(text), -100  ; Shift_JIS 文字列を Unicode 文字列にした時の長さを取得
        unilen = stat                                                       ; これも文字数が返る(ヌル文字分含む)
        // Unicode に変換
        text2 = text                ; text 変数へ Unicode 文字列を入れるために Shift_JIS 文字列を別の変数に複製
        sdim text, unilen * 2       ; Unicode 文字列が入るだけの大きさで変数を作り直す
        unilen --                   ; ヌル文字分減らす
        cnvstow text, text2
        mes "\nファイルの内容 (ShiftJISからUnicodeに変換して表示) : "
    }

    // ファイルの内容を一部表示してみる
    rect = 0GINFO_CY640GINFO_CY + 16 * 3
    DrawTextW hdcvarptr(text), -1varptr(rect), DT_WORDBREAK|DT_EXPANDTABS
    pos , rect(3)

    // ファイル情報を表示
    mes "\nファイルサイズ:\n" + filesize + "byte"
    mes "\n文字数:\n" + unilen + "文字"
    mes "\n文字列のサイズ:\n" + (unilen * 2) + "byte"
    redraw

標準のHSPのファイルオープンダイアログ命令 dialog "", 16 で得られるファイルパスの文字コードは Shift_JIS なので、Win32API の GetOpenFileNameW から Unicode 用のファイルオープンダイアログを開いて、Unicode 文字列でファイルパスを取得しています。
ファイルの内容を取得する際も、HSP 標準の bload, noteload 命令には Shift_JIS 文字列のファイルパスしか渡せないため、API の CreateFileW で Unicode 文字列のファイルパスから ファイルを開き、これまた API の ReadFile で読み込んでいます。
テキストファイルの内容を取得した後も、その中身が同じ文字コードで書かれた文字列とは限らないため 中の文字コードによって処理を分けています。処理を分けるためにはそのテキストの文字コードの種類を判定する必要がありますが、それには API の IsTextUnicode 関数で実現できるようなので それを使って見ました(ネット上の C言語で書かれたいくつかのサンプルを見ながら書いて見ましたがいまいち使い方に自信が無い...(^^;))。
このスクリプトでは中のテキストの文字コードが Unicode ならそのまま、Shift_JIS なら Unicode に変換し、API の DrawTextW を使って Unicode 文字列を画面に表示しています。

・デスクトップにあるファイル名の一覧を Unicode で取得し表示する
#スクリプトは HSP3.4rc1 で実行確認しています

#include "user32.as"
#include "kernel32.as"
#include "gdi32.as"

#define MAX_PATH                    $00000104
#define DT_WORDBREAK                $00000010
#define DT_EXPANDTABS               $00000040
#define INVALID_HANDLE_VALUE        $FFFFFFFF
#define ERROR_NO_MORE_FILES         $00000012
#define FILE_ATTRIBUTE_DIRECTORY    $00000010
#define SW_SHOW                     $00000005
#define WS_CHILD                    $40000000
#define WS_VISIBLE                  $10000000
#define WS_BORDER                   $00800000
#define WS_VSCROLL                  $00200000
#define WS_HSCROLL                  $00100000
#define ES_MULTILINE                $00000004
#define ES_READONLY                 $00000800
#define WS_EX_CLIENTEDGE            $00000200
#define FW_NORMAL                   $00000190
#define DEFAULT_CHARSET             $00000001
#define OUT_DEFAULT_PRECIS          $00000000
#define CLIP_DEFAULT_PRECIS         $00000000
#define DEFAULT_QUALITY             $00000000
#define DEFAULT_PITCH               $00000000
#define FF_DONTCARE                 $00000000
#define WM_SETFONT                  $00000030
#define SYSTEM_FONT                 $0000000D

#define dwFileAttributes            0
#define nFileSizeLow                8
#define cFileName                   11


// ファイル列挙の準備
    // FindFirstFileW に必要な物の準備
    // WIN32_FIND_DATA は 320 byte だが、Unicode 版の WIN32_FIND_DATAW は 592 byte 必要
    dim fd, 148                                                 ; WIN32_FIND_DATAW 構造体分の領域確保
    sdim dir, MAX_PATH * 2
    cnvstow dir, DIR_DESKTOP + "\\*"                            ; FindFirstFileW に渡すフォルダ名を Unicode 文字列として用意

// ファイル列挙の開始
    // 検索ハンドルと最初のファイルを取得
    FindFirstFileW varptr(dir), varptr(fd)
    if stat = INVALID_HANDLE_VALUE {
        dialog "ファイル列挙開始のエラー"
        end
    }
    hfind = stat

    // ファイルリスト作成に必要な物の準備
    sdim filelist, (MAX_PATH * 2 + 64) * 256                    ; ファイル一覧の入る変数(予め十分なサイズを取る)
    sdim tmp, 64                                                ; 一時的に使う変数
    sdim ln, 64
    cnvstow ln, "\n"                                            ; 改行を Unicode 文字化する
    num = 0

    // ファイルリストを作る
    repeat
        // 取得ファイル(フォルダ)名を filelist に書き込む
        lstrcatW varptr(filelist), varptr(fd.cFileName)         ; filelist 末尾に fd.cFileName からの文字を書き込む

        // ファイルかフォルダかで処理を分ける
        if fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY {
            cnvstow tmp, "(フォルダ)"                         ; tmp にフォルダ表記を Unicode 文字列として格納
        } else {
            cnvstow tmp, strf("(%dバイト)", fd.nFileSizeLow)  ; tmp にファイルサイズを Unicode 文字列として格納
        }

        // ファイルリスト変数に改行区切りで追加
        // 取得したのがファイルならファイルサイズが、フォルダならフォルダ表記が書き込まれる
        lstrcatW varptr(filelist), varptr(tmp)                  ; filelist 末尾にファイル情報(サイズ、フォルダ)を書き込む
        lstrcatW varptr(filelist), varptr(ln)                   ; filelist 末尾に改行を書き込む

        // 次のファイルを取得
        FindNextFileW hfind, varptr(fd)
        if stat = 0 {                                           ; 処理の終了判定
            GetLastError
            if stat = ERROR_NO_MORE_FILES {                     ; ファイルが尽きたかエラーかを判定
                num = cnt + 1                                   ; 列挙数を格納
            } else {                                            ; エラーが起きたので終了する
                num = -1
            }
            break
        }
    loop

// ファイル列挙の終了
    // 検索ハンドルを閉じる
    FindClose hfind

    title "列挙数 : " + num
    if num < 0 {
        mes "次のファイルを取得のエラー"
        stop
    }


// 表示のしかたを切り替え可能
#if 0;1
// ウィンドウに直接ファイルリストを描画
    font MSGOTHIC16
    rect = 00GINFO_WINXGINFO_WINY : pos , rect(3)
    DrawTextW hdcvarptr(filelist), -1varptr(rect), DT_WORDBREAK|DT_EXPANDTABS
    redraw
#else
// HSPで言うところの mesbox を作成して、ファイルリストを表示
    // エディットコントロール作成
    style = WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_BORDER | WS_VSCROLL |WS_HSCROLL | ES_READONLY
    CreateWindowExW WS_EX_CLIENTEDGE, "EDIT"varptr(filelist), style, 00GINFO_WINXGINFO_WINYhwnd0hinstance0
    hedit = stat                                                ; コントロールのハンドル
    // エディットにフォントを設定
    sdim fontname, MAX_PATH * 2
    cnvstow fontname, MSGOTHIC                                  ; フォント名をユニコードにしておく
    pandf = DEFAULT_PITCH | FF_DONTCARE
    CreateFontW 16000, FW_NORMAL, 000, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, pandf, varptr(fontname)
    hfont = stat                                                ; フォントのハンドル
    SendMessageW hedit, WM_SETFONT, hfont, 1                    ; フォントを反映
// 一応最後にフォントを消去
    onexit *FIN
    stop
*FIN
    DeleteObject hfont
    end
#endif

列挙したデスクトップ上のファイル名の連結には Unicode のため a += b + "\n" とかは出来ないので、初めはサイズをチマチマ計算し memcpy 命令を使っての切り貼りで連結していましたが、lstrcatW と言う便利なAPI関数を発見してだいぶんスッキリ書くことが出来ました。
結果を表示するメッセージボックス(EDIT)も、HSP標準の物では Unicode 文字列を表示できないので、Unicode 対応のオブジェクト(コントロール)を、API の CreateWindowExW から作成しています。

・おまけで Unicode の表を表示。キーボードの上下左右キーでスクロール。
#スクリプトは HSP3.4rc1 で実行確認しています

#include "gdi32.as"

    start = 0
    fsize = 24
    xmax = GINFO_WINX / fsize
    ymax = GINFO_WINY / fsize
    font "メイリオ", fsize
    gosub *SUB_PRINT
    onkey gosub *ON_KEY
    stop

*ON_KEY
    if wparam >= 37 & wparam <= 40  {
        if wparam = 37  :page -= ymax
        if wparam = 39  :page += ymax
        if wparam = 38  :page--
        if wparam = 40  :page++
        gosub *SUB_PRINT
    }
    return

*SUB_PRINT
    redraw 0
    color 255255255  :boxf
    color
    repeat xmax * ymax
        x = cnt \ xmax
        y = cnt / xmax
        wpoke text, 0, start + cnt + page * xmax
        TextOutW hdc, x * fsize, y * fsize, varptr(text), 1
    loop
    redraw 1
    title ""strf("0x%04x ~ 0x%04x", (start + page * xmax) & 0xffff, (start + page * xmax + xmax * ymax) & 0xffff)
    return


ここまで作ってみて、やはり HSP から Unicode を扱うのはかなり面倒だという事が分かりました。HSPの命令は Unicode にはほぼ無力なので、手軽さが売りの HSP でやるには、APIを多用したり定数を定義したり、あまりにも面倒臭すぎます。。。

#調べながらなので間違い等あるかもしれません。
#HSPスクリプトの色分けには主に eller さん作の HTXcnv を使わせて頂きました。
関連記事

コメント

掲載しているスクリプトを久しぶりに見返したところ、あまりにもひどかったので修正しました。
あんないい加減な物を長い間掲載してしまって申し訳ないです。
ついでに記事内の文章の一部も加筆修正しました。
これらのユニコードを手軽に扱えるモジュールを自サイトにて公開しました。説明を見てもよく分からなかったり、分かっても面倒くさいと思っていた人は一度使ってみると良いかもしれません。
http://dendama.wordswaves.net/hsp/module/module.html#mod_unicode

コメントの投稿

   管理者にだけ表示を許可する
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。