2バイト目に'\'(0x5c) を含むSJIS文字

文字列操作において、対象とする文字列にSJIS文字が入っているとさまざまな問題を引き起こす。ここでは、その例としてファイル名を扱っている場合をあげ、その対処方法について述べる。

 

フォルダ名の取得

ファイルのフルパス名から、そのフォルダ名を切り出す場合を考える。

string strFullPathName = "C:\\DOC\\一覧.TXT";
string strFolderPathName;

string::size_type pos = strFullPathName.rfind('\\');
if(pos != string::npos) strFolderPathName = strFullPathName.substr(0, pos);

このとき、strFolderPathName は"C:\DOC"である。しかし、

string strFullPathName = "C:\\DOC\\一覧表.TXT";
string strFolderPathName;

string::size_type pos = strFullPathName.rfind('\\');
if(pos != string::npos) strFolderPathName = strFullPathName.substr(0, pos);

では、strFolderPathNameの値は、"C:\DOC\一覧 "となり、フォルダ名の抽出に失敗する。これは、ファイル名をあらわす文字の中に2バイト目が0x5cのものが入っているためである。

 

2バイト目'\'(0x5c) を含むSJIS文字一覧

"―ソЫ\噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭xx"

 

対処方法

文字の先頭からSJIS文字を避けながら、'\'を探し、最後に見つかった位置を使用する。

 

サンプルプログラム

ファイル名については0x5cだけを気をつければ済むが、スクリプト処理などで他の記号が混ざる場合は、それを考慮する必要がある。以下にそれを行うためのサンプルプログラムをあげ、前述の問題の対処例を示します。

// SJIS文字の1バイト目のコード
const BYTE SJIS1_S1 = 0x81;
const BYTE SJIS1_E1 = 0x9f;
const BYTE SJIS1_S2 = 0xe0;
const BYTE SJIS1_E2 = 0xf5;

// SJIS文字の2バイト目のコード
const BYTE SJIS2_S1 = 0x40;
const BYTE SJIS2_E1 = 0x7e;
const BYTE SJIS2_S2 = 0x80;
const BYTE SJIS2_E2 = 0xfc;

BOOL IsSJIS1(BYTE c)
{
    // 普通に書けばこのような感じだが、高速化する
    // if((c >= SJIS1_S1 && c <= SJIS1_E1) || (c >= SJIS1_S2 && c <= SJIS1_E2)) return TRUE;
    c ^= 0x20;
    if(c >= (SJIS1_S1 ^ 0x20) && c <= (SJIS1_E2 ^ 0x20)) return TRUE;
    return FALSE;
}

BOOL IsSJIS2(BYTE c)
{
    if((c >= SJIS2_S1 && c <= SJIS2_E1) || (c >= SJIS2_S2 && c <= SJIS2_E2)) return TRUE;
    return FALSE;
}

// SJIS文字を除外して文字を検索、最初に見つかった位置を返す
int SJIS_StrChrSearch(const char *s, char c)
{
    // c == 0 も考慮している
    for(int i = 0; ; i++){
        if(s[i] == c) return i;
        if(s[i] == '\0') break;
        if(IsSJIS1(s[i]) && IsSJIS2(s[i + 1])) i++;
    }
    return -1;


// SJIS文字を除外して文字を検索、最後に見つかった位置を返す
int SJIS_StrChrSearchLast(const char *s, char c)
{
    int nLast = -1;
    // c == 0 も考慮している
    for(int i = 0; ; i++){
        if(s[i] == c) nLast = i;
        if(s[i] == '\0') break;
        if(IsSJIS1(s[i]) && IsSJIS2(s[i + 1])) i++;
    }
    return nLast;

サンプルを使用した対処例

string strFullPathName = "C:\\DOC\\一覧表.TXT";
string strFolderPathName;

int nPos = SJIS_StrChrSearchLast(strFullPathName.c_str(), '\\');
if(nPos != -1) strFolderPathName = strFullPathName.substr(0, nPos);