• ベストアンサー

C/C++言語で日本語処理

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」 } あと、詳しい書籍等もご存知の方教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.6

参考までに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; }

tobasu
質問者

補足

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?は難しいですね。 良い書籍も探しています。

その他の回答 (6)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.7

> 8は表示されましたが、以下は何も表示されませんでした。 Visual C++でコンパイルしたのでは? std::wcoutはバグがあることが多いので、別の方法を採ったほうがよいかもしれませんね。 例えば、 printf("%ls", moji.substr(3, 5).c_str()); とか。 > STL?は難しいですね。 STLは一切使っていません。

tobasu
質問者

お礼

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

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.5

★追記。 ・前回の(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; } 以上。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★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 のような関数はないので。 ・以上。

  • borazu
  • ベストアンサー率53% (8/15)
回答No.3

汎用関数 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
noname#50176
noname#50176
回答No.2

#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; } のようにコード体系に合わせると良いかと思います。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

> 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バイト目を判別して、処理するしかありません。 ただし、処理系を特定できるのであれば、この限りではありません。

関連するQ&A

  • c言語の文字列の逆順のプログラムがわかりません

    文字列を逆順して出力するプログラミングがわかりません。 #include <stdio.h> #include <string.h> void reverse(char *moji, char *gyaku); int main(void) { char x[30]; char y[30]; puts("文字を入力してください。\n"); scanf("%s", x); reverse(x, y); printf("逆順すると%sです。\n", y); return (0); } void reverse(char *moji, char *gyaku) { int i, len; len = strlen(moji); gyaku = moji + len - 1; for(i = 0; i < len; i ++){ putchar((int)*gyaku); gyaku--; } } 理想とする実行結果は 文字を入力してください。 abcdefg 逆順するとgfedcbaです。 なんですが、 上記のソースを実行すると 文字を入力してください。 abcdefg gfedcba逆順すると(謎の漢字)です。 となります。 どこがおかしいんでしょうか? よろしくおねがいします。

  • C言語の標準関数についての質問です。

    C言語の標準関数についての質問です。 実行結果が以下のようになるようにしたいのですが・・・ char moji[]="AB$c5kDp9#s*Gz"; ←この二行は用意されている変数です。 int cnt[4]; 実行結果↓ 英大文字:4 ←cnt[0] 英小文字:5 ←cnt[1] 数字文字:2 ←cnt[2] その他:3 ←cnt[3] 使える関数は ・isdigit ・islower ・isupper です。 ↓ 自分で作ったのですがなかなかできません>< #include <stdio.h> #include <ctype.h> void main(void) { char moji[]="AB$c5kDp9#s*Gz"; int cnt[4]={0,0,0,0},flg; cnt=0; while(flg!='\0') { flg=isupper(moji[cnt]); if(flg!=0) { cnt[0]++; } flg=islower(moji[cnt]); if(flg!=0) { cnt[1]++; } flg=flgisdigit(moji[cnt]); if(flg!=0) { cnt[2]++; } else { cnt[3]++; } } printf("英大文字:%d\n",cnt[0]); printf("英小文字:%d\n",cnt[1]); printf("数字文字:%d\n",cnt[2]); printf("その他:%d\n",cnt[3]); return; } プログラムの訂正や、他にどんなプログラムがあるのか見せてもらいたいです>< ちなみに自分のはflgを立てるやり方なのですが、エラーが・・・・。

  • ソースコードの間違い (C言語)

    変数に、文字列を入れた配列の文字列の最後の要素数を入れたいのですが(つまり'\0')、うまくいきません。いつも2個多い値になってしまいます。 #include <stdio.h> void main() { char moji[100]={0}; int c=0; fgets(moji,sizeof moji,stdin); while( moji[c] != '\0' ) ++c; printf("\n%d\n",c); // } 例えば5文字の1ビット文字を入れると、最後の文字はmoji[4]にあるのでprintfで4と表示されるはずじゃないですか。でも6になるんです。いつも+2の値になるんですよ。どうやらfgetsを使っているからそうなるらしく、scanfを使うと結果は1多い値に、普通に配列に直接文字列を代入すると正常な結果になります。別にcに-2してもいいのですが、それはなんだか癪といいますか・・・。なぜこういうことがおきるのでしょうか?回答よろしくお願いします。

  • C言語の質問です><

    C言語の質問です>< 次のような実行結果が得られるプログラミングをしたいのですが・・ ちなみにポインタや標準関数のstrシリーズは使用不可です。 文字列1:ABCDEFGHIJ 開始位置:0 文字数:3 文字列2:ABC 開始位置が7で文字数が5とかの場合は'¥0'の位置まで表示するようにしたいのですが、自分の以下のプログラムだと開始位置が0で文字が3だとABCと表示できるのですが、開始位置が7で文字数が5とかだとできません>< #include<stdio.h> void main(void) { char m1[]="ABCDEFGHIL"; char m2[11]; int i,j,start,mozikazu; printf("文字列1:%s\n",m1); printf("開始位置:"); scanf("%d",&start); if(start >=0 && start <11) { printf("文字数:"); scanf("%d",&mozikazu); } if((start+mozikazu)<11) { for(i=0;i<mozikazu;i++) { m2[i]=m1[i]+start; } m2[i]='\0'; } printf("文字列2:%s\n",m2); return; } どうか教えてください><

  • 皆様に、C言語についての質問です・・><

    皆様に、C言語についての質問です・・>< この前も質問しましが、まだできないので>< 後、少し問題にもミスがあったので書き直しました プログラミングの内容は以下のようなものです。 以下の実行結果をもとにプログラミングしなさい。 char m[30]; 実行結果 文字,個数==>A,5 文字,個数==>b,3 文字,個数==>c,1 文字,個数==>E,2 文字,個数==>^Z 文字列:AAAAAbbbcEE ↓は自分でやったプログラムです>< #include<stdio.h> void main(void) { char m[30],moji; int kosu,i; printf("文字,個数==>"); fflush(stdin); scanf("%c,%d",&moji,&kosu); i=0; while(m[i]!=EOF) { printf("文字,個数==>"); scanf("%c,%d\n",&moji,&kosu); m[i]=moji; i++; } m[i]='\0'; printf("文字列:%s\n",m); return; } ^z押しても終了できません>< 表示もおかしいです・・・・ 訂正お願いいたします><

  • C言語の問題がわかりません。

    C言語の問題がわかりません。 ファイルを読み込んで、文字数と単語数を数えるプログラムなのですが、 例えば、ファイルが 「I was born in Japan  I like baseball」でしたら、 1:I was born in Japan 19文字、5単語 2:I like baseball 15文字、3単語 と表示したいのですが、下記のプログラムのままだと、 1:I was born in Japan 20文字、5単語 2:I like baseball 15文字、3単語 と作った文章の改行の部分を認証してしまいます。 改行の部分を認証しないようにこれを修正するにはどうしたらいいでしょうか? また、もう一つあるのですが、結果の文章を 1:napaJ・・・ 2:・・・ekil I と行ごとに逆に表示したいのですが、どうすれば逆に表示できますでしょうか? #include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { FILE *fin; char filename[20]; char data[256], *abc; int a,b,n; printf("ファイル名の入力 :"); scanf("%s", filename); fin=fopen(filename,"r"); if(fin == NULL){ printf("%sがオープンできません!\n",filename); exit(1); } a=0; b=0; n=0; while(fgets(data,256,fin) != NULL) { a=a+1; b=strlen(data); abc = data; *(abc - 2) = '\t'; while (*abc == ' ') {abc++;} while (*abc != '\0') { while (*abc != '\0' && !(*abc == ' ' || *abc == '\t' || *abc == ',' || *abc == '.')){abc++;} n = n+1; while (*abc != '\0' && (*abc == ' ' || *abc == '\t' || *abc == ',' || *abc == '.')) {abc++;} } printf("%d:%s\n",a,data); printf("%d文字、%d単語\n",b,n); n=0; } fclose(fin); return 0; }

  • 処理系によって。

    プログラムの作成をしたのですが、 自宅(VisualC++)だと実行できるのに、 学校の端末(OSしかわかりませんが、UNIXです。) だと、core dumpedしてしまいます。 どういう原因が考えられるでしょうか? プログラムソースは次の通りです。 ―――――――――――――――――――――――――― #include<stdio.h> #include<stdlib.h> char *strtok(char *moji, char cc); void main() { char moji[200]; char kugiri[10]; char *cp; printf("文字列を入力してください\n"); scanf("%s",moji); printf("区切り文字を入力してください\n"); scanf("%s",kugiri); cp=strtok(moji,kugiri[0]); printf("取り出した文字列は%s\n",cp); while(cp!=NULL){ cp=strtok(NULL,kugiri[0]); printf("取り出した文字列は%s\n",cp); } } char *strtok(char *moji,char cc) { int len; char *cp1,*cp2; char *mem; static char *moji_old; if(moji==NULL){ moji=moji_old; }      len=0; cp1=moji; while(*cp1!='\0'){ len++; cp1++; } mem=malloc(len+3); if(mem==NULL){ puts("メモリが足りません\n"); return NULL; } cp1=moji; cp2=mem; while(*cp1!=cc && *cp1!='\0'){ *cp2=*cp1; cp1++; cp2++; } free(mem); if(*cp1=='\0'){ moji_old=cp1; mem=NULL; } else{ moji_old=cp1+1; *cp2='\0'; } return mem; } ―――――――――――――――――――――――――― 以上です。

  • C言語で分からないところがあるのですが

    すみません。C言語のポインタで分からないことがあって来ました。 ポインタの理屈は理解してはいるのですが、いざソースコードを書いてみようということになると全く手がつけられずにいます。 以下のソースコードですが、strlen()と同じ働きをする関数mystrlen()と、strcmp()と同じ働きをする関数mystrcmpを、ポインタを使って作成するものです。どこをどうすればいいのか教えてくださいませんか。 #include <stdio.h> int main(void) { char str1[80], str2[80]; int i, j; int len1, len2; printf("第1の文字列を入力してください: "); gets(str1); printf("第2の文字列を入力してください: "); gets(str2); /* * 文字列の長さを確認する */ /* NULL文字(文字列の最後)まで読み飛ばす */ for (len1 = 0; len1 < 80 && str1[len1] != '\0'; len1++) ; /* ループ終了後、len1 に文字列の長さが入っている */ if (len1 < 80) { printf("%s は %d 文字の長さです\n", str1, len1); } else { printf ("第1の文字列が80字以上あります\n"); } /* str2 についても同様 */ for (len2 = 0; len2 < 80 && str2[len2] != '\0'; len2++) ; if (len2 < 80) { printf("%s は %d 文字の長さです\n", str2, len2); } else { printf ("第2の文字列が80字以上あります\n"); } if (len1 < 80 && len2 < 80) { for (i = 0; i < 80 && str1[i] != '\0' && str2[i] != '\0' && str1[i] == str2[i]; i++) ; if (str1[i] == str2[i]) { /* 両者同時に == '\0' のはず*/ printf("文字列は等しい\n"); } else if (str1[i] < str2[i]) { /* str1[i] == '\0' のはず*/ printf("%s は %s より小さい\n", str1, str2); } else { /* str2[i] == '\0' のはず*/ printf("%s は %s より大きい\n", str1, str2); } } /* * 十分なスペースがあれば、str2をstr1の最後に連結する */ if (len1 + len2 < 80) { /* str1 の末尾を探す */ for (i = 0; str1[i] != '\0'; i++) ; /* ループを抜けた段階では i は len1 と同じはずなので、 上記のループを作らず、i の代わりに len1 を用いるのも可 */ /* それ以降に str2 の中身をコピーする */ for (j = 0; str2[j] != '\0'; j++) { str1[i+j] = str2[j]; } str1[i+j] = '\0'; printf("%s\n", str1); } else { printf ("文字列をつなげた長さが80字以上あります\n"); } /* * str2をstr1にコピーする */ if (len1 + len2 < 80) { for (i = 0; str1[i] != '\0'; i++) { str1[i] = str2[i]; } str1[i] = '\0'; printf("%s %s\n", str1, str2); } return 0; }

  • C言語での文字列ソート動作について

    任意の文字列を入力し、その文字列を昇順にソートするプログラムを作ったのですが、入力する文字の文字数が大きく異なると期待した結果が得られません。 文字数が少なくなったり、他の配列の文字が混ざったりと言う結果に成ってしまっています。 何が原因か分からない状態です。 以下にサンプルを記載させて頂きますので、助言よろしくお願いします。 /*----------------------------------------- 入力例 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC BBBBBBBBBBBBBBBB AAAAAA -----------------------------------------*/ #include <stdio.h> #include <string.h> #include <stdlib.h> void swapc(char *cx , char *cy){ char tmp[100]; strcpy(tmp, cx); strcpy(cx, cy); strcpy(cy, tmp); } int main(){ char *num[100]; char str_tmp[100]; //文字列一時格納 int moji_cnt; //入力した文字列のカウント int n , m; // 文字列入力処理開始 printf("文字列を入力してください\n"); for( moji_cnt = 0 ; moji_cnt != 3 ; moji_cnt++){ scanf("%s", str_tmp); *(num+moji_cnt) = (char *)malloc(sizeof(char) * (strlen(str_tmp)+1)); //メモリ確保 strcpy(*(num+moji_cnt), str_tmp); } puts("\n"); // 文字数ソート処理 for(n = 0 ; n < moji_cnt-1 ; n++){ for(m = 1 ; m < moji_cnt-n ; m++){ if(strcmp(*(num+n) , *(num+n+m)) > 0){ swapc(*(num+n) , *(num+n+m)); // 文字列入れ替え } } } puts("\n"); for(n = 0;n != moji_cnt;n++){ printf("%s\n" , *(num+n)); } free(num); }

  • C言語

    forの直後で1+2+3+4+5+・・・・・・・と加算し続ける式がわからないので教えてください。 #include<stdio.h> int main(void) { char moji; int i,sum; printf("正の整数を1から順に加算します。n\"); printf("加算を開始してよろしいですか。(Y=実行。N=終了)\n"); moji=getchar(); if(moji==y) { for(i=2;sum>=1001;i++) { この部分がわかりません; printf("加算値は%dです。¥n",sum); } }else if(moji=='n'){ printf("終了します。\n"); }else{ printf("YまたはNを入力してください。\n"); } return 0; }

専門家に質問してみよう