テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
かめやん

テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#1

投稿記事 by かめやん » 14年前

課題でドラックアンドドロップされたファイルの文書の全角カタカナを半角カタカナに変換するというソフトが思うとおりに動きません。
ご協力をお願いします。

ファイルの文字コードはS-jisです。
ドラックアンドドロップをしてテキストファイルを読み込むところまではうまくいきました。
テキストファイルを1バイトずつ読み込んで、全角カタカナの領域である「0x83」の判定を読み込むことはできるのですが、それ以降がうまくいきません。

テキストファイルの文字列に関するアドレスに関する知識不足だとは思うのですが、考えても分かりません。
以下に、コードを載せさせていただきますので解決だけでなく改良もあれば教えてください。
よろしくお願いします。

コード:

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mbstring.h>
#define BYTE unsigned char

char *getStrTailPoint(char *);
char *getDotPoint(char *,char *);
char* changeCode(int);
void  StringCat2(char *, char *);
 
void main(int argc,char** argv )
{
	//tail…パス+ファイル名のENDポイントアドレス
	//dot…拡張子のポイントアドレス
	char *tail ;
	char *dot;
	//bfp…BaseFilePointAddress
	//mfp…MakedFilePointAddress
	FILE *bfp,*mfp;
	//makedFileName…作られるファイルの名前
	char  makedFileName[1000];
	static int i;

	//ファイルからの文字を格納する一時格納する変数
	char *r,tmp[3];
	int p;
	BYTE q;

	if(argv[1]!=NULL){
		//	argv[1]…ドラックアンドドロップされたファイルとパスが格納
		//ファイルを開く
		if(NULL ==(bfp=fopen(argv[1],"r")))
		{
			 printf("指定されたファイルを開けません");
			 getchar();
			 exit(1);
		}
		
		tail = getStrTailPoint(argv[1]);
		dot  = getDotPoint(argv[1],tail);
		
		 
		//makeFileNameに"."までの元の名前を格納
		for(i = 0;&argv[1][i]<=dot;i++){
			if(&argv[1][i]<dot){
				makedFileName[i]=argv[1][i];
			}else{
				makedFileName[i]='\0';
			}
		}
		
		//makeFileNameに"__"と拡張子を挿入
		 strcat(makedFileName,"__");
		 strcat(makedFileName,dot);

		//新規ファイルの作成
		if(NULL ==(mfp=fopen(makedFileName,"w"))){
			printf("新規ファイルを作れません");
			getchar();
			exit(1);
		}
		//既存ファイルから新規ファイルにテキストをコピー
		//新規ファイルの全角文字を半角文字へ
		while((p = fgetc(bfp)) != EOF)
		{

			q=(BYTE)p;
			if(0x83==q){
				/*r=changeCode(p);
				fputs(r,mfp);
				break;*/
				fputc('2',mfp);
				continue;
			}
			fputc(p,mfp);
		}
		//ファイルを閉じる
		fclose(bfp);
		fclose(mfp);

		
	}

	return ;
}

/*
*getStrTailPoint  
*文字列のENDポイントアドレスを調べる関数
*/
char *getStrTailPoint(char *string){
	char *p;
	int i ;
	int len = strlen(string);
	for(i=0;i<=len+1;i++){
		if(*string=='\0'){
			p=string;
			return p;
		}
		string++;
	}
	return string;
}


/*
*getDotPoint 
*ファイルの拡張子の名前が始まるポイントアドレスを検出する関数
*/
char *getDotPoint(char *string,char *tail){
	char *p;
	int i;
	int len = strlen(string);

	for(i=0;i<len;i++){
		if(*tail=='.'){
			p=tail;
			return p;
		}
		tail--;
	 
	}
	return tail;
}

char* changeCode(int ch){
	char p[3];
	 
	char *zenkaku ={"アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォッャュョガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポ\0"};
	char *hankaku ={"アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォッャュョ\0"};
	char *hankaku2 ={"カキクケコサシスセソタチツテト"};
	char *hankaku3 ={"ハヒフヘホ"};
	char *dakutenn ={"゙゚"};
	


	do{
		if(*zenkaku<='ョ'||ch==*zenkaku){
			p[0]=*hankaku;
			p[1]='\0';
			return p;
			break;
		}else if(ch==*zenkaku){
			if(*zenkaku<='ド'){
				p[0]=*hankaku2;
				p[1]=*dakutenn;
				p[2]='\0';
				return p;
				break;
			}else{
				if(*zenkaku<='ボ'){
					p[0]=*hankaku3;
					p[1]=*dakutenn;
					p[2]='\0';
					return p;
					break;
				}else{
					p[0]=*hankaku3;
					p[1]=*(++dakutenn);
					p[2]='\0';
					return p;
					break;
				}
				hankaku3++;
			}
		 hankaku2++;
		}
		zenkaku++;
		if(*hankaku!='\0')
			hankaku++;
	}while(*zenkaku=='\0');
//	return ch;
}


アバター
a5ua
記事: 199
登録日時: 15年前

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#2

投稿記事 by a5ua » 14年前

このような方針はどうでしょうか?

コード:

char *full[] = {"ア", "イ", "ウ", "エ", "オ", /* 略 */};	// 全角カタカナのリスト
char *half[] = {"ア", "イ", "ウ", "エ", "オ", /* 略 */};		// 対応する半角カタカナ

char line[1000];	// 1行分のバッファ

// 1行ずつ読み込む
while (fgets(line, 1000, fp)) {
   /* line中のfull[i]をhalf[i]に置換する */
   /* lineをファイルに出力 */
}
上のように、全角←→半角の1対1対応のテーブルを作っておけば、
あとは、文字列を置換する処理を作ればできるはずです。
置換処理は自分で考えてみてください。


かめやん
記事: 2
登録日時: 14年前
住所: 広島

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#4

投稿記事 by かめやん » 14年前

> a5uaさん
ありがとうございます。
そのように、自分のchangeCode関数については変えてみようと思います。

nonさんがおっしゃてくれた濁音、半濁音についても疑問はあるのですが、 a5uaさんが考えてほしいという置換処理というのは全角から半角に変えたときに発生するポインターのずれのことですか?

そのあたりも把握してないので場違いな意見でしたら申し訳ございません。
ただ、2バイトから1バイトに変換するときに1行ずつ読み取る形にしますと、1バイト分小さくなるために次の文字のアドレスを1バイト分だけ前に進める必要があるのではないかと思っています。

もっと考えてみますが、知恵を貸していただけたら幸いです。

アバター
a5ua
記事: 199
登録日時: 15年前

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#5

投稿記事 by a5ua » 14年前

コード:

char *full[] = {"ア", "イ", "ウ", "エ", "オ", /* 略 */, "ポ"};  // 全角カタカナのリスト
char *half[] = {"ア", "イ", "ウ", "エ", "オ", /* 略 */, "ポ"};      // 対応する半角カタカナ
濁音、半濁音に対しても、同様に対応付けられると思います。


以下に、例題とともに置換処理のアルゴリズム(のヒント)を示します。
char target[100] = "abcアdef";
char before[] = "ア";
char after[] = "ア";
target中のbeforeをafterに置換したいとします。

targetからbeforeを探索し、以下のような結果を得ます。

コード:

abcアdef
|  | |
p  m s
p, m, sは文字列のある位置を指すポインタとします。

このとき、結果となる文字列は以下のような手順で構成できます。
結果を格納する変数をresultとすると
・p以降の(m - p)文字をresultにコピー(result = "abc")
・resultにafterを追加(result = "abcア")
・resultにs以降を追加(result = "abcアdef")

文字列の探索にはstrstrを使います。
文字数を指定してコピーはstrncpyを使います。

かずま

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#6

投稿記事 by かずま » 14年前

かめやん さんが書きました: テキストファイルを1バイトずつ読み込んで、全角カタカナの領域である「0x83」の判定を読み込むことはできるのですが、それ以降がうまくいきません。
2バイト目が 0x83 の文字がありますから、それではだめです。
a5ua さんが書きました:文字列の探索にはstrstrを使います。
strstr は使えないでしょう。次の場合を考えてください。

char target[100] = "宴会";
char before[] = "ラ";
char after[] = "ラ";

"宴会" は 0x89 0x83 0x89 0xef です。
"ラ" は 0x83 0x89 です。

次のプログラムは、2バイト目をちゃんとチェックしていないなど、いい加減なところがありますが、一応、半角カタカナになると思います。
"「」、。・"などの記号は半角になりません。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int is_kanji(unsigned char c) { return (c ^ 0x20) - 0xA1u < 60; }

char han[] = "ァアィイゥウェエォオガギグゲゴザジズゼゾダヂッヅデドナニヌネノ"
             "バ゚ビ゚ブ゚ベ゚ボ゚マミ.ムメモャヤュユョヨラリルレロ.ワ..ヲン";

int main(int argc, char **argv)
{
    char *p, name[1024];  int c;  FILE *fp0, *fp1;

    if (argc != 2) printf("no first argument\n"), exit(1); 
    p = strrchr(argv[1], '.');
    if (p) c = p - argv[1];
    else c = strlen(argv[1]), p = "";
    sprintf(name, "%.*s__%s", c, argv[1], p);

    fp0 = fopen(argv[1], "r");
    if (!fp0) printf("can't open %s\n", argv[1]), exit(2);

    fp1 = fopen(name, "w");
    if (!fp1) printf("can't create %s\n", name), exit(3);

    while (c = fgetc(fp0), c != EOF) {
        if (is_kanji(c)) {
            if (c == 0x83) {
                c = fgetc(fp0);
                if (c >= 0x40 && c <= 0x93) {
                    int d = c - 0x40;
                    c = han[d];
                    if (c == '゙') fputc(han[d-1], fp1);
                    else if (c == '゚') fputc(han[d-2], fp1);
                    else if (c == '.') fputc(0x83, fp1), c = d + 0x40;
                }
                else if (c == 0x94) fputc('ウ', fp1), c = '゙';
                else fputc(0x83, fp1);
            } else fputc(c, fp1), c = fgetc(fp0);
        }
        fputc(c, fp1);
    }
    fclose(fp0), fclose(fp1);
    return 0;
}

アバター
a5ua
記事: 199
登録日時: 15年前

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#7

投稿記事 by a5ua » 14年前

かずま さんが書きました: strstr は使えないでしょう。次の場合を考えてください。

char target[100] = "宴会";
char before[] = "ラ";
char after[] = "ラ";

"宴会" は 0x89 0x83 0x89 0xef です。
"ラ" は 0x83 0x89 です。
確かに、これはだめですね。
代替案としては、wchar_tを使うことでしょうか。

No.5で書いたものは
char → wchar_t
"ア" → L"ア"
strstr → wcsstr
などと読み替えてください。

かずま

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#8

投稿記事 by かずま » 14年前

a5ua さんが書きました: No.5で書いたものは
char → wchar_t
"ア" → L"ア"
strstr → wcsstr
などと読み替えてください。
wchar_t target[100] = L"半角カタカナに変換"; に対して、

 wcsstr(target, L"ア");
 wcsstr(target, L"イ");
  :
 wcsstr(target, L"ヲ");
 wcsstr(target, L"ン");

を何度も繰り返すんですか?

wcsstr(target, L"カ"); で見つかって result に L"半角カタカナに変換" が
入っても、まだ全角の「カ」がありますから、もう一度
wcscpy(target, result); wcsstr(target, L"カ"); が必要ですよね。

まあ、別の使い方をすれば wcsstr も使えるとは思うんですが、具体的にはどうすればいいんでしょうか?

さて、私が No.6 で示したプログラムは while ループの中がごちゃごちゃしていたので、もう少しすっきりさせてみました。
「ヴ」を han[] の中に 'v' として入れました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char **argv)
{
    static char han[] = "ァアィイゥウェエォオガギグゲゴザジズゼゾダヂッヅデド"
                "ナニヌネノバ゚ビ゚ブ゚ベ゚ボ゚マミ.ムメモャヤュユョヨラリルレロ.ワ..ヲンv";
    char *p, name[1024];  int c;  FILE *fp0, *fp1;

    if (argc != 2) printf("no first argument\n"), exit(1); 
    if ((p = strrchr(argv[1], '.')) != NULL) c = p - argv[1];
    else c = strlen(argv[1]), p = "";
    sprintf(name, "%.*s__%s", c, argv[1], p);

    fp0 = fopen(argv[1], "r");
    if (!fp0) printf("can't open %s\n", argv[1]), exit(2);

    fp1 = fopen(name, "w");
    if (!fp1) printf("can't create %s\n", name), exit(3);

    while ((c = fgetc(fp0)) != EOF) {
        if ((c ^ 0x20) - 0xa1u < 60) {  // c is a leading byte
            int d = fgetc(fp0);
            if (c != 0x83 || d < 0x40 || d > 0x94) fputc(c, fp1), c = d;
            else if ((c = han[d-0x40]) == '.') fputc(0x83, fp1), c = d;
            else if (c == '゙') fputc(han[d-0x41], fp1);
            else if (c == '゚') fputc(han[d-0x42], fp1);
            else if (c == 'v') fputc('ウ', fp1), c = '゙';
        }
        fputc(c, fp1);
    }
    fclose(fp0), fclose(fp1);
    return 0;
}

アバター
a5ua
記事: 199
登録日時: 15年前

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#9

投稿記事 by a5ua » 14年前

かずま さんが書きました: wchar_t target[100] = L"半角カタカナに変換"; に対して、

 wcsstr(target, L"ア");
 wcsstr(target, L"イ");
  :
 wcsstr(target, L"ヲ");
 wcsstr(target, L"ン");

を何度も繰り返すんですか?
まあ、その通りです。変換対象がなくなるまでループを回しています。
効率は悪いかもしれませんが、実装が簡単だと思ったので提案しました。

コードも載せておきます。
► スポイラーを表示
最後に編集したユーザー a5ua on 2011年7月02日(土) 17:15 [ 編集 1 回目 ]

かずま

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#10

投稿記事 by かずま » 14年前

全角カタカナはすべて 1文字ですから、次のように before を wchar_t で渡せば wcschr が使えて、wcsstr の出番はなくなります。

コード:

wchar_t *replace_all(wchar_t *s, wchar_t before, const wchar_t *after)
{
   (省略)
    while (pos = wcschr(s + offset, before)) {
        wchar_t *suffix = pos + 1;
   (省略)
wchar_t *full2half(wchar_t *s)
{
    const wchar_t FULL[80] = {
        L'ア', L'イ', L'ウ', L'エ', L'オ', 
after_len は 1 か 2 ですが、1 の場合、memmove するのは無駄ですし、
その memmove の使い方はバグですよ。次の main を実行すると、

コード:

int main(int argc, char *argv[])
{
    wchar_t s[100] = L"例題とともに置換処理の";
 
    setlocale(LC_CTYPE, "jpn");
    
    printf("%ls\n", s);
    printf("%ls\n", full2half(s));

	wcscpy(s, L"アルゴリズムを");
    printf("%ls\n", s);
    printf("%ls\n", full2half(s));
 
    return 0;
}
実行結果は

コード:

例題とともに置換処理の
例題とともに置換処理の
アルゴリズムを
アルゴリズムを儀理の
memmove の第3引数は、wcslen(suffix) * sizeof(wchar_t) + 1 ではなく、
(wcslen(suffix) + 1) * sizeof(wchar_t) ですね。

さて、私のほうは、もう少し改良したものをお見せしましょう。
ファイルのオープン・クローズは本質ではないので標準入出力にしてみました。

コード:

#include <stdio.h>
 
int main(void)
{
    static char han[] =
        ".ァ.ア.ィ.イ.ゥ.ウ.ェ.エ.ォ.オ.カガ.キギ.クグ.ケゲ.コゴ.サザ.シジ.スズ.セゼ.ソゾ"
        ".タダ.チヂ.ッ.ツヅ.テデ.トド.ナ.ニ.ヌ.ネ.ノ.ハバパ.ヒビピ.フブプ.ヘベペ.ホボポ"
        ".マ.ミ\x83\x7f.ム.メ.モ.ャ.ヤ.ュ.ユ.ョ.ヨ.ラ.リ.ル.レ.ロヮ.ワヰヱ.ヲ.ンヴ";
    int c, d;  char *p;

    while ((c = getchar()) != EOF) {
        if ((c ^ 0x20) - 0xa1u < 60) {  // c is a leading byte
            d = getchar();
            if (c != 0x83 || d < 0x40 || d > 0x94) putchar(c), c = d;
            else p = han + (d-0x40)*2, *p != '.' && putchar(*p), c = p[1];
        }
        putchar(c);
    }
    return 0;
}

かずま

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#11

投稿記事 by かずま » 14年前

あっ、間違えた。
han[] の最後は、 「ヴ」ではなくて、「ヴ」です。

かずま

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#12

投稿記事 by かずま » 14年前

最初の質問者はどうして、「解決!」にしてくれないのだろう。
これでもまだダメでしょうか?

コード:

#include <stdio.h>
 
int main(void)
{
    static char han[][3] = { "ァ","ア","ィ","イ","ゥ","ウ","ェ","エ","ォ","オ","カ",
        "ガ","キ","ギ","ク","グ","ケ","ゲ","コ","ゴ","サ","ザ","シ","ジ","ス","ズ",
        "セ","ゼ","ソ","ゾ","タ","ダ","チ","ヂ","ッ","ツ","ヅ","テ","デ","ト","ド",
        "ナ","ニ","ヌ","ネ","ノ","ハ","バ","パ","ヒ","ビ","ピ","フ","ブ","プ","ヘ",
        "ベ","ペ","ホ","ボ","ポ","マ","ミ","\x83\x7f","ム","メ","モ","ャ","ヤ","ュ",
        "ユ","ョ","ヨ","ラ","リ","ル","レ","ロ","ヮ","ワ","ヰ","ヱ","ヲ","ン","ヴ" };
    int c, d;
    while ((c = getchar()) != EOF)
        if ((c ^ 0x20) - 0xa1u >= 60) putchar(c);
        else if (d = getchar(), c != 0x83 || d < 0x40 || d > 0x94)
            putchar(c), putchar(d);
        else fputs(han[d - 0x40], stdout);
    return 0;
}

かめやん
記事: 2
登録日時: 14年前
住所: 広島

Re: テキストファイルの全角カタカナー>半角カタカナに変換ソフトを作ってます

#13

投稿記事 by かめやん » 14年前

返事が遅くなり申し訳ございません
考えていただいた皆様、ありがとうございます。
特にa5uaさんと、かずまさんのソースは本当に助かりました。
ともに試してみました。

お二人のソースをもっと理解をしてみようと思っています。
本当にありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る