C言語関係掲示板

過去ログ

No.157.16進表記のデータを文字表記したい


No.1002

データが16進表記のデータを文字表記したい
投稿者---ひよこ(2002/02/02 21:24:02)


データが16進表記でそのデータを文字表記したいのですが、
下記の方法を使うと、「@(40)」「*(2a)」「|(7c)」
等の場合の変換式はどうなるのでしょうか?
下記()のプログラムと同じような考え方のようですが、
応用がききません。

if(c >='20' && c =<'29'){
return(計算式)
}
else if(c >='2a' && c =<'2f'){
return(計算式)
}
else if(c >='30' && c =<'39'){
return(計算式)
}
else if(c >='3a' && c =<'3f'){
return(計算式)
}
else if(c >='40' && c =<'49'){
return(計算式)
}
else if(c >='4a' && c =<'4f'){
return(計算式)
}
...
...
else if(c >='7a' && c =<'7e')

と項目分けをしたのですが。。
この項目分けも変でしょうか?
混合して計算は無理だと思い、
記号部分と数値部分を分けたのですが
混合計算は可能でしょうか?

char1文字を16進数とみなし、そのバイナリ値を返す
int ascii_to_hex(){
char c;

if (c >='0' && c =<'9')
return(c - '0');

else if (c >='A' && c <='f')
return(c - 'A'+10);

else if (c >='a' && c =<'f')
return(c - 'a'+10);
}
どうぞ宜しくお願いします。

No.1003

Re:データが16進表記のデータを文字表記したい
投稿者---shu(2002/02/02 22:05:50)


>データが16進表記でそのデータを文字表記したいのですが、
>下記の方法を使うと、「@(40)」「*(2a)」「|(7c)」
>等の場合の変換式はどうなるのでしょうか?
>下記()のプログラムと同じような考え方のようですが、
>応用がききません。
> ...

質問の内容がはっきりとはわかりませんが、
sprintf()関数で解決できないでしょうか?
詳しいことは、トップページの索引から調べてみてください。

No.1004

Re:データが16進表記のデータを文字表記したい
投稿者---ひよこ(2002/02/03 00:20:47)


>質問の内容がはっきりとはわかりませんが、
>sprintf()関数で解決できないでしょうか?
分かりにくい表現でした、すみません。
表記というか変換をしたいのです。
16進数→文字表示というように。
例えば、21→! A→41 5c→\ h→68 7e→{
というように制御文字を除いた20〜7eまでを文字に変換したいのです。
strtol()関数で解決できそう(?)なのですが、
計算式で表現できないものでしょうか??
switch文で一行ずつ変換できるのかもしれないですが、
とても膨大な行に渡ってしまうのでちょっと悩んでます。
ビット計算なんかで解決したりするものなのでしょうか??
すみませんが、どうぞよろしくお願いします!

No.1005

Re:データが16進表記のデータを文字表記したい
投稿者---kikk(2002/02/03 02:42:37)


ども。


>16進数→文字表示というように。
>例えば、21→! A→41 5c→\ h→68 7e→{
>というように制御文字を除いた20〜7eまでを文字に変換したいのです。
>strtol()関数で解決できそう(?)なのですが、
>計算式で表現できないものでしょうか??

「21」というのが文字列の"21"なのか整数型の0x21なのかがわかりませんが
前者の場合はおっしゃる通り、strtol()を使えばいいです。後者の場合は
特に何もしなくてもいいです。場合によってはchar型へのキャストが必要
かもしれません。出力時に文字出力関数を使うか、printf()系なら%cを指定
すれば!が出力されます(ASCIIならば)。


何進数か、という問題は、人間が認識できる表記法の問題であって、計算機
の内部では何進数という概念はありません。ハードウェアレベルまで考える
と無いということはないのですけど。。以下のコードを理解の参考にして
みてください。

char c1='!', c2=0x21, c3=33;
printf("%c %c %c\n", c1, c2, c3); /* その数値に対応する文字を出力 */
printf("%x %x %x\n", c1, c2, c3); /* 16進数で出力 */
printf("%d %d %d\n", c1, c2, c3); /* 10進数で出力 */


では。

No.1006

Re:データが16進表記のデータを文字表記したい
投稿者---ひよこ(2002/02/03 09:51:56)


ありがとうございます!

>「21」というのが文字列の"21"なのか整数型の0x21なのかがわかりませんが
>前者の場合はおっしゃる通り、strtol()を使えばいいです。後者の場合は
>特に何もしなくてもいいです。場合によってはchar型へのキャストが必要
>かもしれません。
文字列の"21"なのです。strtolの場合はポインタのポインタを
使わなければならないようなので、ちょっと応用がきかないのです。。。

>何進数か、という問題は、人間が認識できる表記法の問題であって、計算機
>の内部では何進数という概念はありません。ハードウェアレベルまで考える
>と無いということはないのですけど。。
そうだったのですね。。
本当によく分かっていないのです。勉強になりました。

>char c1='!', c2=0x21, c3=33;
>printf("%c %c %c\n", c1, c2, c3); /* その数値に対応する文字を出力 */
>printf("%x %x %x\n", c1, c2, c3); /* 16進数で出力 */
>printf("%d %d %d\n", c1, c2, c3); /* 10進数で出力 */
変換したい文字数が3つだけならよいのですが、
そうではないので、下記☆のように項目分けをしたいのです。
しかし、この方法ではできないのでしょうか?

もし「char '42'(=16進数表現)」が判別されるとしたら、
42(文字でのB)の場合、
42−41+10 = 11(16進数でのBにあたる)
もし「char '30'(=16進数表現)」が判別されるとしたら、
30(文字での0)の場合、
30−30(=16進では30) = 0(16進数での0にあたる)
16進数→文字に変換したいのです。
しかし、上記の式はちょっとおかしいのでしょうか?
どこかのHP等で見て、考え方を理解したつもりだったのですが、
記号の場合(?や/等)の変換方法はどう表現したらよいのかがわからないのです。


if(c >='20' && c =<'29'){
return(計算式)
}
else if(c >='2a' && c =<'2f'){
return(計算式)
}
else if(c >='30' && c =<'39'){
return(計算式)
}
...
...
else if(c >='7a' && c =<'7e')){
return(計算式)
}



No.1007

Re:データが16進表記のデータを文字表記したい
投稿者---shu(2002/02/03 12:52:46)


ちょっと一言
c言語では、
'' は ascii文字、一文字に対して使います、
"" は文字列に対して、16進数の場合は、0x を付けて表記します。

なので、ひよこさんのやっている
if(c >='20' && c =<'29')
のような表現方法は間違っています。


char c1='!', c2=0x21, c3=33;
printf("%d %c %x\n", c1, c1, c1);
printf("%d %c %x\n", c2, c2, c2);
printf("%d %c %x\n", c3, c3, c3);

上記の3通りのprintfは、全く同じ結果がでます。
ASCIIコード表を参考に考えてみてください。

No.1008

Re:データが16進表記のデータを文字表記したい
投稿者---ひよこ(2002/02/03 13:58:41)


>c言語では、
>'' は ascii文字、一文字に対して使います、
>"" は文字列に対して、16進数の場合は、0x を付けて表記します。
>
>なので、ひよこさんのやっている
>if(c >='20' && c =<'29')
>のような表現方法は間違っています。
>
>char c1='!', c2=0x21, c3=33;
>printf("%d %c %x\n", c1, c1, c1);
>printf("%d %c %x\n", c2, c2, c2);
>printf("%d %c %x\n", c3, c3, c3);
>
>上記の3通りのprintfは、全く同じ結果がでます。
>ASCIIコード表を参考に考えてみてください。
ありがとうございます。本当に基礎的なことが分かっていないもので
申し訳ありません。。

でも、これを式として表現はできないものでしょうか?
実は画面表示させるのではなく、
変換処理後、構造体に入れるという処理がしたいのです。
色々なデータ(例えば、eメールアドレス)を入れるため、記号がきたときに
変換できるようにしたいのです。
一行ずつ、switch文で処理もできるかと思いますが、
記号全部をひと作業ずつやると膨大な行のソースになってしまいます。。

分かりにくい質問で申し訳ありません。

No.1012

16進表記が入っている文字列を数値に変換
投稿者---kikk(2002/02/04 03:01:34)


ども。


>>if(c >='20' && c =<'29')

というような表現から推測するに、URLでの16進数表記みたいなものをさして
いるのでしょうか?2桁ずつの16進数(の文字列)を数値として扱うというような。

もしそうなのならば、以下のようにすればいいと思います。エラーチェック
が甘いので(マクロのあたり)あしからず。

/* 10から15は'A'〜'F'(大文字)とする */
#define HexToInt(X) (((X)>='0'&&(X)<='9')?((X)-'0'):((X)-'A'+10))

int value, i;
char str[]="31"; /* 変換元の文字列 */

value = 0;
for (i=0; i<2; i++) {
value *= 16; /* 16進1桁分ずらす。<<= 4でも可 */
value += HexToInt(str[i]); /* |=でも可 */
}

putchar(value); /* !が表示される(ASCII) */

参考までに。
もし、10進2桁を変換する場合はfor文の中身は以下のようになるでしょう。

value *= 10; /* 10進1桁分ずらす */
value += str[i]-'0';


strtol()を使う場合は、必要な部分(2桁分)だけ適当な配列にコピーして、
それを引数にすればOKです。コピー時に末尾に'\0'をつけるのを忘れずに。
strtol()の使い方はライブラリリファレンスを参照してください。また、
過去ログ
http://f1.aaa.livedoor.jp/~pointc/log135.html
も参考にしてください。なお、strtol()の第2引数(ポインタのポインタ)を
うまく使えば、コピーしなくて済ませられるようなコードが書けるかも
しれません。


では。

No.1013

Re:16進表記が入っている文字列を数値に変換
投稿者---ひよこ(2002/02/04 18:07:24)


表現が間違っていたようなのですが、cはchar型で大きさ3の配列です。

#define HexToInt(X) (((X)>='0'&&(X)<='9')?((X)-'0'):((X)-'A'+10))
すみません。このソースの読み取りができないのですが。。。

それと、「16進1桁分ずらす」というのはなぜそういう作業をするのでしょうか?

せっかく答えて頂いたのに、理解する能力がなくて申し訳ありません。


No.1019

Re:16進表記が入っている文字列を数値に変換
投稿者---ともじ(2002/02/04 23:27:18)


こんばんは。

>#define HexToInt(X) (((X)>='0'&&(X)<='9')?((X)-'0'):((X)-'A'+10))
>すみません。このソースの読み取りができないのですが。。。

これは、マクロに条件演算子を組み込んだもので、if文で表すと、
	if(X >= '0' && X <= '9') 
		X = X - '0';
	else 
		X = X - 'A' + 10;

になります。

>それと、「16進1桁分ずらす」というのはなぜそういう作業をするのでしょうか?

もとの数値が仮に0xABCDだった場合、
0xABCD = 10 * 16^3 + 11 * 16^2 + 12 * 16^1 + 13 * 16^0
  (16^3 は 16の3乗と読んでください)
になりますよね。
つまり、'0'〜'9'なら整数0〜9に変換、
    'A'〜'F'なら整数10〜15に変換し、
桁が上がるたびに16を掛けながら加算していくわけです。

ところで、ひよこさんがやりたい処理、今一つわからないのですが、
"2A+1C"という文字列を、演算式 0x2A+0x1Cに変換し、結果の0x46を
得たい、というものでしょうか。

No.1021

Re:16進表記が入っている文字列を数値に変換
投稿者---ひよこ(2002/02/05 01:37:51)


本当にご迷惑おかけしております。。
ありがとうございます。

>ところで、ひよこさんがやりたい処理、今一つわからないのですが、
>"2A+1C"という文字列を、演算式 0x2A+0x1Cに変換し、結果の0x46を
>得たい、というものでしょうか
char型の配列の文字(16進表記のもの)を変換してそれを文字表現したいのです。
しかし、画面に表示させるのではなく、またそれからの処理があるのですが。。
なぜはじめの文字が配列になっているのか?というのは、
(0xを省いた16進表現。そんなのないですか?)
データファイルから(fgetsで)読みこんだ文字列をつなぎあわせた為、
そうなっているのです。
((こんな面倒な方法ではなく、tokenを使ってとりだせばよいのでしょうか??))

16進ダンプの反対版のカンジではないかと思っているのですが。。

最初提示した
if(c >="20" && c =<"29"){
return(計算式)
}
else if(c >="2a" && c =<"2f"){
return(計算式)
}
else if(c >="30" && c =<"39"){
return(計算式)
}
...
...
else if(c >="7a" && c =<"7e")){
return(計算式)
}
で解決させようと格闘しており、
文字と数値の計算をどうしようかとずっと悩んでおりました。

もし、
16進文字列(最後にヌルが入っている為)が30であった場合、
30(16進表示) - 30 = 0(文字の0)
なので、変換後の値 = c - 30
(30〜39の間であったら、この式で変換できる?)
16進文字列(最後にヌルが入っている為)が42であった場合、
42(16進表示) - 'A'(41) + 10 = 11(16進のB)
なので、変換後の値 = c - 'A' + 10
(41〜49の間であったら、この式で変換できる?)
というようになるのかと思ったのですが、
これが、!や*になると表現の仕方が分からないのです。
計算式がごちゃごちゃに混ざっているようですが、
結果的に文字として表現できればよいのです。

かえって?な表現になってしまったでしょうか?
どうぞ宜しくお願いします。


No.1024

Re:16進表記が入っている文字列を数値に変換
投稿者---ともじ(2002/02/05 12:24:20)


仕事中なので取り急ぎ要点のみ。

>if(c >="20" && c =<"29"){
>return(計算式)
>}

これは、strcmp関数を使って、
if (strcmp(c,"20")>=0 && strcmp(c,"29")<=0)
になります。


No.1025

Re:16進表記が入っている文字列を数値に変換
投稿者---B.Smith(2002/02/05 13:00:42)


こんにちは。

16進数の数値文字列はキャラクタコードを表しているので、それを一文字文のキャラクタとして表示できるように変換したい。ただし、数値文字列中には記号等の文字も含まれる。
ということでしょうか?

>16進文字列(最後にヌルが入っている為)が30であった場合、
>30(16進表示) - 30 = 0(文字の0)
>なので、変換後の値 = c - 30
>16進文字列(最後にヌルが入っている為)が42であった場合、
>42(16進表示) - 'A'(41) + 10 = 11(16進のB)
>なので、変換後の値 = c - 'A' + 10
>というようになるのかと思ったのですが、
>これが、!や*になると表現の仕方が分からないのです。

多分、16進数を表すもの以外の文字を中心に考えたために、一文字単位で見ているのだと思うのですが、

>例えば、21→! A→41 5c→\ h→68 7e→{
>というように制御文字を除いた20〜7eまでを文字に変換したいのです。

今回は16進数の数値変換は2文字のペアで考えてください。その方がずっと簡単です。
入力対象から2文字分の文字を拾ってきて、それを16進数に変換するようにします。cに格納する時、16進数以外の文字だったら、それを格納しないようにします。完全に16進数の文字列であるならば、変換時に記号等を意識する必要はありません。
例.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define     HEX_LEN     2  /* 16進数を表す文字数 */

void    Example(char *);

void    main(void )
{
    char    Str[] = "21@41#5c*68$7E/";

    Example(Str);
}

void    Example(char *Str)
{
    int     Idx,Idx2;
    int     xDigit;
    char    c[HEX_LEN+1] = ""; /* 初期化しておく */

    /* 入力文字列長分繰り返す */
    for(Idx = Idx2 = 0;Str[Idx] != '\0';Idx++){

        /* 現在の文字が16進数でなければ以下の処理を飛ばし、 */
        /* 次の文字に移る                                   */
        if (isxdigit(Str[Idx]) == 0)
            continue;

        /* 16進数文字をcに格納 */
        c[Idx2] = Str[Idx];
        Idx2++;

        /* 2バイト分そろったら、数値に変換する */
        if (Idx2 >= HEX_LEN){
            xDigit = (int )strtol(c,NULL,16);/* 16進数文字列を数値に変換 */
            Idx2 = 0;   /* cのインデックスをリセット */

            /* 変換した数値の処理 */
            /* ここでは表示させています */
            printf("%x  %c\n",xDigit,xDigit);

            /* 次の処理のため、cをヌル文字でクリア */
            c[0] = c[1] = '\0';
        }
    }

    /* cに一文字でも入っていたら、それを変換する */
    if (Idx2 != 0){
        xDigit = (int )strtol(c,NULL,16);   /* 16進数文字列を数値に変換 */

        /* 変換した数値の処理 */
        /* ここでは表示させています */
        printf("%x  %c\n",xDigit,xDigit);
    }
}

関数isxdigitにより16進数の文字と、それ以外の文字を分類します。これで、cには16進数を表現する文字しか格納できなくなります。後は数値に変換するだけです。
数値への変換は、ここでは関数strtolを使用していますが、貴方はすでに変換ロジックを知っているはずですから、それに置き換えてもかまいません。関数strtolの2番目の引数は、エラーチェック等に使用しますが、今回は必ず16進数の文字列が入ってきますので、使用する必要はありません(NULLを指定します)。


No.1031

皆さん、ありがとうございます!
投稿者---ひよこ(2002/02/07 02:11:16)


shuさん、kikkさん、ともじさん、B.Smithさん、
本当に大変ありがとうございました。
今自分の頭の中で整理中です。今度の連休中に
しっかりと完成&理解したいと思います。
今回も色々なポイントを皆さんより教えて頂き、
とても勉強になりました〜!!
なんとか解決できそうな気がします。
大変お世話になりました!

戻る


「初心者のためのポイント学習C言語」 Last modified:2002.03.16
Copyright(c) 2000-2002 TOMOJI All Rights Reserved