プロが教える店舗&オフィスのセキュリティ対策術

C言語orC++言語で日本語の処理がしたいのですが方法がわかりません。
以下のlen,mid関数を作成したいのですが、どうすればよいのでしょうか。
文字コードはSJIS(CP932?)とします。

また、C言語のソースは基本的にSJIS書き、コンパイルすると解釈してよいですか?WindowsはSJIS? LinuxはEUC-JP??

#include <stdio.h>
#include <string.h>

void main(void){
char moji[] = "パソコンでABC";

// この結果は「パソコンでABC」, 19, 18で正しいです。
printf("%s\n%d\n%d", moji, sizeof(moji), strlen(moji));

// 文字数換算で8と出力させる方法
// 汎用関数 Len関数を作る Lenbでない。この場合 len(moji)の結果は「8」

// 文字数換算で6文字目から3文字数カットしたABCを出力する。
// 汎用的に MID関数を作る MIDBでない。この場合 mid(moji, 3, 5)の結果は 「コンでABC」

}

あと、詳しい書籍等もご存知の方教えてください。

A 回答 (7件)

参考までにGNU C++の場合を書いてみます。



ソースファイルでCP932を使う場合、コンパイルオプションで-finput-charset=cp932を指定する必要があります。実行文字コードは、ロケールにあわせて-fexec-charsetで指定してください。(デフォルトではUTF-8になります)

内部的な処理は、多バイト文字列のままでは面倒なので、いったんワイド文字列に変更します。mbstowcs等で変換するか、libiconvでも使いましょう。(もちろん、ICUなどの外部ライブラリでもOKですし、std::codecvtファセットを使ってもOK)
ワイド文字列になってしまえば、あとはstd::wstringを使えば簡単です。

VBのLenは、std::wstring::sizeまたはstd::wstring::lengthを使います。
VBのMidは、std::wstring::substrを使うか、std:wstringのコンストラクタで対応できます。

以下、未検証ですが、コードをのせておきます。

#include <iostream>
#include <string>
#include <locale>
#include <clocale>
#include <cstdlib>

int main()
{
 std::setlocale(LC_CTYPE, "");
 std::wcout.imbue(std::locale(""));

 wchar_t wcs[16];
 mbstowcs(wcs, "パソコンでABC", sizeof(wcs)/sizeof(wcs[0]));
 std::wstring moji(wcs);

 std::wcout << L"Len(moji): " << moji.length() << std::endl;
 std::wcout << L"Mid(moji, 3, 5): << moji.substr(3, 5) << std::endl;
}

この回答への補足

8は表示されましたが、以下は何も表示されませんでした。
ちなみに"が抜けていましたので、付加しています。 [, 3, 5):]

std::wcout << L"Mid(moji, 3, 5):" << moji.substr(3, 5) << std::endl;

substrを返す型が base_string型?なので、printf("%s", moji.substr(3, 5));でも表示されません。

STL?は難しいですね。
良い書籍も探しています。

補足日時:2007/09/28 20:35
    • good
    • 0

> 8は表示されましたが、以下は何も表示されませんでした。



Visual C++でコンパイルしたのでは?
std::wcoutはバグがあることが多いので、別の方法を採ったほうがよいかもしれませんね。
例えば、

printf("%ls", moji.substr(3, 5).c_str());

とか。

> STL?は難しいですね。

STLは一切使っていません。
    • good
    • 0
この回答へのお礼

助かりました。すごいですね。

皆さんのアドバイスも参考になりました。
ありがとうございました。

お礼日時:2007/10/02 00:07

★追記。


・前回の(1)~(4)をサンプルとして載せておく。
 なお、実際には C++ のクラスとして作ればよい。
 MFC の CString 型と同じような使い方になるように。
 ちなみに CString 型でも Unicode 文字を扱えます。

// 独自の文字型
typedef unsigned short JAPANCODE;
// 漢字の第1文字の判定
#define isKanji(c) ((unsigned int)(((unsigned char)(c) ^ 0x20) - 0xA1) < 0x3C)

サンプル1:SJIS文字列から独自文字列に変換
JAPANCODE *tobasu_ToJapan( JAPANCODE japan[], const char string[] )
{
 JAPANCODE *j = japan;
 const char *p = string;
 
 while ( *p != '\0' ){
  if ( isKanji(*p) ){
   *j++ = ((JAPANCODE)p[0] << 8) | p[1];
   p += 2;
  }
  else{
   *j++ = *p++;
  }
 }
 *j = 0x0000;
 return japan;
}

サンプル2:独自文字列からSJIS文字列に変換
char *tobasu_ToSjis( JAPANCODE japan[], char buff[] )
{
 JAPANCODE *j = japan;
 char *p = buff;
 
 while ( *j != 0x0000 ){
  if ( *j > 0x100 ){
   *p++ = (char)(*j >> 8);
  }
  *p++ = (char)(*j >> 0);
  j++;
 }
 *p = '\0';
 return buff;
}

サンプル3:独自文字列の長さ取得
size_t tobasu_Len( JAPANCODE japan[] )
{
 JAPANCODE *p;
 
 for ( p = japan ; *p != 0x0000 ; p++ ){
  ;
 }
 return (size_t)(p - japan);
}
※tobasu_LenBというバイト長の関数も用意した方が良いかも。

サンプル4:独自文字列の中間取り出し
JAPANCODE *tobasu_Mid( JAPANCODE japan[], JAPANCODE middle[], int pos, int n )
{
 JAPANCODE *j = japan + pos - 1;
 JAPANCODE *p = middle;
 
 if ( j < (japan + tobasu_Len(japan)) ){
  while ( (n > 0) && (*j != 0x0000) ){
   *p++ = *j++;
   n--;
  }
 }
 *p = 0x0000;
 return middle;
}

使い方:
int main( void )
{
 JAPANCODE moji[ 100 ];
 JAPANCODE mid[ 100 ];
 char buff[ 200 ];
 // SJISをセット
 tobasu_ToJapan( moji, "パソコンでABC" );
 // 文字列
 printf( "Str=%s\n", tobasu_ToSjis(moji,buff) );
 // 文字数
 printf( "Len=%d\n", tobasu_Len(moji) );
 // 取り出し
 tobasu_Mid( moji, mid, 3, 5 );
 printf( "Mid=%s\n", tobasu_ToSjis(mid,buff) );
 return 0;
}
以上。
    • good
    • 0

★Unicode文字を使わないなら次の方法は。


・SJISコード体系で半角文字、全角文字のそれぞれ1文字を unsigned short 型で表現します。
 この型の配列で文字列を表現する簡易版の日本語処理ライブラリを用意します。
 用意する関数群としては
 (1)SJIS文字列から独自文字列に変換
 (2)独自文字列からSJIS文字列に変換
 (3)独自文字列の長さ取得(Len)
 (4)独自文字列の中間取り出し(Mid)
 (5)独自文字列のコピー
 (6)独自文字列の比較
 (7)独自文字列の検索
 その他はいろいろと独自文字列の処理関数を用意します。

その他:
・本当なら内部では Unicode 文字体系で処理をして入出力を SJIS コードの
 相互変換する仕組みにすればいいと思います。
 Windows 系や Unix 系の両方で動かすなら Unicode 文字をお勧めします。
 Unicode 文字を使えば上記のライブラリは特に必要ないです。
 Unicode 文字を操作する関数群が用意されていますので。
 ただし BASIC で用意されている MID、MIDB はご自分で用意して下さい。
 MID のような関数はないので。
・以上。
    • good
    • 0

汎用関数 Len関数を作ってみました。

半角文字、全角文字が混ざっていても数えられるようにしてみました。

#include <stdio.h>
#include <string.h>

int Len(char *moji);

int main(void)
{
char moji[] = "パソコンでABCabc1009";

printf("%s\n%d\n%d\n", moji, sizeof(moji), strlen(moji));

printf("文字数換算 : %d\n", Len(moji));

return 0;
}

/* 文字数換算 自作関数 */
int Len(char *str)
{
int mojisuu = 0;

while(*str){
/*-------------------------------------------
2バイト文字は1バイト目の値が
0x81~0x9fまたは0xe0~0xfcという
ルールを使用して判定してます。
参考URL:http://tokyo.cool.ne.jp/sdl/index9.html
-------------------------------------------*/
if (0x81 <= *str && *str <= 0x9f || 0xE0 <= *str && *str <= 0xFC) {
str += 2;
} else {
str += 1;
}
printf("%s\n", str); /* 処理内容確認のため いらなければコメントアウトで。。。*/
mojisuu++;
}

return mojisuu;
}

参考URL:http://tokyo.cool.ne.jp/sdl/index9.html
    • good
    • 0

#include <stdio.h>



// mid(元文字列バッファ、開始位置、範囲文字数、結果バッファ、バッファサイズ)
int mid(const char* inStr,short start,short range,char *outStr,int cSize)
{
int i,j,seek;

if (cSize<=range || start<=0) return 0;
for (i=0,seek=1;seek-start;i+=(inStr[i]<0x20||inStr[i]>0x7E)?2:1) seek++;
for (j=0,seek=1;seek<=range;)
{
if (inStr[i])
if (inStr[i]<0x20||inStr[i]>0x7E)
{
outStr[j]=inStr[i];
outStr[j+1]=inStr[i+1];
i+=2,j+=2;
}
else
{
outStr[j++]=inStr[i++];
}
else return 0;
seek++;
}
outStr[j]=0;
return 1;
}
// 文字数=Len(元文字列バッファ)
int Len(char* inStr)
{
int i,len;

for (i=0,len=0;inStr[i];i+=(inStr[i]<0x20||inStr[i]>0x7E)?2:1) len++;
return len;
}

int main()
{
char str[]="パソコンでABC",buf[20];

printf("%s の文字数は、%d 文字です\n",str,Len(str));
if (mid(str,3,6,buf,20))
printf("%s \n",buf);
else
printf("サイズが合いません\n");
if (mid(str,3,6,buf,5))
printf("%s \n",buf);
else
printf("サイズが合いません\n");
if (mid(str,2,8,buf,20))
printf("%s \n",buf);
else
printf("サイズが合いません\n");
return 0;
}

のようにコード体系に合わせると良いかと思います。
    • good
    • 0

> C言語のソースは基本的にSJIS書き、コンパイルすると解釈してよいですか?



処理系によります、といいたいところですが、確実に特定の文字コードにするためには、拡張表記を用いて文字コードを直接記述する必要があります。

> char moji[] = "パソコンでABC";

ではなく、

char moji[] = "\x83\x70\x83\x5c\x83\x52\83\x93\x82\xc5ABC";

とします。

> len,mid関数

Visual Basicの同名関数でしょうか?
だとすると、いったんUnicodeに変換する必要があります。処理系を特定しないのであれば、自分でSJIS ←→ Unicodeの変換関数を作る必要があります。
いったんUnicodeに変換してしまえば、あとは多バイト文字を意識しなくてもよいはずです。出力時に、再度SJISに戻す必要があるでしょうが。

Unicodeに変換せず、多バイト文字のまま扱うのであれば、自分でSJISの1バイト目、2バイト目を判別して、処理するしかありません。
ただし、処理系を特定できるのであれば、この限りではありません。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!