C言語関係掲示板

過去ログ

No.1030 半角、全角文字が混在した文字列からサイズ分取得

[戻る] [ホームページ]
No.13181

半角、全角文字が混在した文字列からサイズ分取得
投稿者---太一(2004/03/15 23:15:49)


半角、全角文字が混在した文字列が60バイトあります。
その文字列から20バイトずつ取得し、変数に取得格納
したいのですが、その際、20バイト目が全角文字の1バイト
目の時は、19バイトまでとし、次にまわしたいのですが
どうすればよいのですか。


No.13182

Re:半角、全角文字が混在した文字列からサイズ分取得
投稿者---YuO(2004/03/16 00:01:11)


>半角、全角文字が混在した文字列が60バイトあります。
>その文字列から20バイトずつ取得し、変数に取得格納
>したいのですが、その際、20バイト目が全角文字の1バイト
>目の時は、19バイトまでとし、次にまわしたいのですが

可変長のエンコーディングを使っている場合に文字を単位とするのは,非常に大変です。
標準入力に対して,最大20バイトを読み取って表示するプログラムを,
真面目に作ると次のようになります。
#エラー処理は行っていないです。不正な多バイト文字列を食わすと暴走します。
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 標準的なバッファ長 */
#define DEF_BUFFER_SIZE 256
/* 入力バッファ長 */
#define INPUT_BUFFER_SIZE DEF_BUFFER_SIZE
/* ワイド文字バッファ長 */
#define CONVERT_BUFFER_SIZE DEF_BUFFER_SIZE
/* 出力バッファ長 */
#define WRITE_BUFFER_SIZE 20
/* 出力時に変換した文字数を調べるためのバッファ長 */
#define TEMPORARY_CONVERT_BUFFER_SIZE WRITE_BUFFER_SIZE

int main (void)
{
    char input_buffer[INPUT_BUFFER_SIZE];

    /* 現在の標準ロケールに設定しておく */
    setlocale(LC_ALL, "");

    while (fgets(input_buffer, INPUT_BUFFER_SIZE, stdin) != 0) {
        wchar_t convert_buffer[CONVERT_BUFFER_SIZE + 1];
        size_t converted_length, i;

        /* 末尾の改行を削除する */
        if (input_buffer[0] != 0 && input_buffer[strlen(input_buffer) - 1] != '\n') {
            input_buffer[strlen(input_buffer) - 1] = 0;
        }

        /* ワイド文字列に変換する */
        converted_length = mbstowcs(convert_buffer, input_buffer, CONVERT_BUFFER_SIZE);
        convert_buffer[converted_length] = 0;

        for (i = 0; i < converted_length;) {
            char output_buffer[WRITE_BUFFER_SIZE + 1];
            wchar_t output_temp_buffer[TEMPORARY_CONVERT_BUFFER_SIZE + 1];
            size_t reconverted_length;

            /* 指定したバイト数だけ切り出す */
            reconverted_length = wcstombs(output_buffer, convert_buffer + i, WRITE_BUFFER_SIZE);
            output_buffer[reconverted_length] = 0;

            /* 出力する */
            puts(output_buffer);

            /* 変換したワイド文字数を取得する */
            i += mbstowcs(output_temp_buffer, output_buffer, TEMPORARY_CONVERT_BUFFER_SIZE);
        }
    }

    return 0;
}

setlocale(LC_ALL, "");で設定される多バイト文字列のエンコーディングが,
対象となる文字列のエンコーディングに等しいことが条件です。
#Open WatcomのようにCロケールしか使えないコンパイラでは不可。

wcstombsはワイド文字側で何文字変換したかをする術が無いので,
C++のstd::codecvt<wchar_t, char, std::mbstate_t>::outに比べて使いにくいです……。
今回は,再度ワイド文字列に変換することで変換したワイド文字列の文字数を取得しています。


No.13183

Re:半角、全角文字が混在した文字列からサイズ分取得
投稿者---太一(2004/03/16 01:29:18)


YuOさんありがとうございます。

環境を書くのを忘れていました。
HP-UX11.00 UNIX-Cです。

No.13189

Re:半角、全角文字が混在した文字列からサイズ分取得
投稿者---太一(2004/03/16 03:39:48)


以下のように、先頭から20バイト、切り捨ての最後の文字が全角文字の
1バイト目だった場合、その直前までを取得は、は何とかできたのですが、
残りの40バイトを上手く処理することができません。
要は、処理させるために使用したもとデータの次から読み込み位置を
返却させ繰り返しで関数を使用できないかと思っているのですが

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

main(int argc,char *argv[])
{
    char str[60] ;
    char *wk_str ;

    strcpy(str,argv[1]) ;
    wk_str = str :

    cut_string(wk_str, cut_str) ;

    printf(in = [%s]\n",str) ;
    printf(out = [%s]\n",cut_str) ;
}

int cut_string(char *in_data,char *out_data)
{
    char w_str[100] ;
    char c ;
    int  i ;

    memset(w_str,'\0',sizeof(w_str)) ;
    i = 0 ;

    while (*in_data) {
        c = *in_data ;
        if ((c & 0xFF) >= 0x81 && (c & 0xFF) <= 0x9F || (c & 0xFF) >= 0xE0 && (c & 0xFF) <= 0xFC) {
            if (i + 2 <= 20) {
                w_str[i] = *in_data++ ;
                w_str[++i] = *in_data++ ;
                i++ ;
             } else {
                breka ;
             }
        } else {
            if (i + 1 <= 20) {
                w_str[i] = *in_data++ ;
                i++ ;
             } else {
                breka ;
             }
        }
    }

    strncpy(out_data,w_str,strlen(w_str)) ;
    *(out_data + strlen(w_str)) = '\0' ;
}




No.13192

Re:半角、全角文字が混在した文字列からサイズ分取得
投稿者---nop(2004/03/16 09:08:49)


>int cut_string(char *in_data,char *out_data)

return が抜けてます。

>breka ;

これは何ですか?


せめてコンパイルが通るリストをコピペしましょうよ。

No.13193

Re:半角、全角文字が混在した文字列からサイズ分取得
投稿者---九十九(2004/03/16 09:42:56)


きれい、スムーズではないですが、
以下でwk_strに処理された位置がかえされます。
あとは、呼び出し側の工夫でできると思います。

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

main(int argc,char *argv[])
{
    char str[128] ;
    char wk_str[128] ;
    char cut_str[21] ;

    memset(str,'\0',sizeof str) ;
    memset(wk_str,'\0',sizeof wk_str) ;
    memset(cut_str,'\0',sizeof cut_str) ;

    strcpy(str,"アイウエオアイウエオ1234512345") ;
    strncpy(wk_str,str,strlen(str)) ;

    cut_string(wk_str,cut_str,10) ;

    printf(" in = [%s]\n",str) ;
    printf("out = [%s]\n",cut_str) ;
    printf(" wk = [%s]\n",wk_str) ;
}

int cut_string(char *in_data,char *out_data,int len)
{
    char w_str[128] ;
    char *tmp_str ;
    char c ;
    int  i ;

    if (strlen(in_data) <=len) {
        strncpy(out_data,in_data,strlen(in_data)) ;
    }

    memset(w_str,'\0',sizeof(w_str)) ;
    i = 0 ;
    tmp_str = in_data ;

    while (*tmp_str) {
        c = *tmp_str ;
        if ((c & 0xFF) >= 0x81 && (c & 0xFF) <= 0x9F || (c & 0xFF) >= 0xE0 && (c & 0xFF) <= 0xFC) {
            if (i + 2 <= len) {
                w_str[i] = *tmp_str++ ;
                w_str[++i] = *tmp_str++ ;
                i++ ;
             } else {
                break ;
             }
        } else {
            if (i + 1 <= len) {
                w_str[i] = *tmp_str++ ;
                i++ ;
             } else {
                break ;
             }
        }
    }

    strncpy(out_data,w_str,strlen(w_str)) ;
    *(out_data + strlen(w_str)) = '\0' ;

    printf("move position [%s]\n",tmp_str) ;

    strncpy(in_data,tmp_str,strlen(tmp_str)) ;
    *(in_data + strlen(tmp_str)) = '\0' ;
}



No.13209

Re:半角、全角文字が混在した文字列からサイズ分取得
投稿者---かずま(2004/03/17 13:26:56)


> 要は、処理させるために使用したもとデータの次から読み込み位置を
> 返却させ繰り返しで関数を使用できないかと思っているのですが
#include <stdio.h>

#define N  20

#define IS_SJIS(c)  (((c) & 0xFF ^ 0x20) - 0xA1u < 60)

int cut_string(const char *s1, int offset, char *s2, int len)
{
    int i = 0;

    while (i < len && s1[offset]) {
        if (IS_SJIS(s1[offset])) {
            if (i == len-1) break;
            s2[i++] = s1[offset++];
        }
        s2[i++] = s1[offset++];
    }
    s2[i] = '\0';
    return offset;
}

int main(int argc, char *argv[])
{
    char buf[N+1];  int i = 0;

    if (argc != 2) return 1;
    while (argv[1][i]) {
        i = cut_string(argv[1], i, buf, N);
        puts(buf);
    }
    return 0;
}


No.13206

ありがとうございます。
投稿者---太一(2004/03/17 02:09:01)


みなさんありがとうございます。

添付したソースも誤っているみたいだし、
もう一度見直してみます。