Cでバイト配列を16進文字列に変換するにはどうすればよいですか?


88

私が持っています:

uint8 buf[] = {0, 1, 10, 11};

printfを使用して文字列を出力できるように、バイト配列を文字列に変換したいと思います。

printf("%s\n", str);

取得します(コロンは必要ありません):

"00:01:0A:0B"

どんな助けでも大歓迎です。


buf[i]キャストしなければならないunsigned char、またはそれがあればオーバーフローするbuf[i] > 127、つまり:buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]);
whatacold

回答:


93
printf("%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3]);

より一般的な方法の場合:

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) printf(":");
    printf("%02X", buf[i]);
}
printf("\n");

文字列に連結するには、これを行う方法がいくつかあります...おそらく、文字列の末尾へのポインタを保持し、sprintfを使用します。また、配列のサイズを追跡して、割り当てられたスペースより大きくならないようにする必要があります。

int i;
char* buf2 = stringbuf;
char* endofbuf = stringbuf + sizeof(stringbuf);
for (i = 0; i < x; i++)
{
    /* i use 5 here since we are going to add at most 
       3 chars, need a space for the end '\n' and need
       a null terminator */
    if (buf2 + 5 < endofbuf)
    {
        if (i > 0)
        {
            buf2 += sprintf(buf2, ":");
        }
        buf2 += sprintf(buf2, "%02X", buf[i]);
    }
}
buf2 += sprintf(buf2, "\n");

ありがとうマーク-私の問題はもう少し複雑です。私は実際にXバイトの長さのバッファを持っています。Xバイトに対してこれを実行し、結果として文字列を取得する一般的な方法を見つけたいと思っていました。
スティーブウォルシュ

xが長さであると仮定して、任意のバイト数を処理するためのコードを追加するように更新されました。
Mark Synowiec 2011年

マークさん、ありがとうございましたが、この問題で最も注意が必要だったのは、これを文字列に出力する方法です。
スティーブウォルシュ2011年

5
printf("%02X", (unsigned char)buf[i]);オリジナルは符号なし文字のオーバーフローを引き起こすため、使用する必要があります
easytiger 2013

3
なぜprintf("%02hhX", buf[i])ですか?
Hintron 2017

32

完全に言えば、重いライブラリ関数(snprintf、strcat、memcpyさえも)を呼び出さなくても簡単に実行できます。たとえば、libcが使用できないマイクロコントローラーまたはOSカーネルをプログラミングしている場合に役立ちます。

あなたがそれをグーグルで検索した場合、あなたが周りに同様のコードを見つけることができる本当に素晴らしいものは何もありません。実際、snprintfを呼び出すよりもはるかに複雑ではなく、はるかに高速です。

#include <stdio.h>

int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    int i = 0;
    for(; i < sizeof(buf)-1; ++i){
        *pout++ = hex[(*pin>>4)&0xF];
        *pout++ = hex[(*pin++)&0xF];
        *pout++ = ':';
    }
    *pout++ = hex[(*pin>>4)&0xF];
    *pout++ = hex[(*pin)&0xF];
    *pout = 0;

    printf("%s\n", str);
}

これはもう少し短いバージョンです。中間インデックス変数iとlasteケースコードの重複を回避するだけです(ただし、終了文字は2回書き込まれます)。

#include <stdio.h>
int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    for(; pin < buf+sizeof(buf); pout+=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
    }
    pout[-1] = 0;

    printf("%s\n", str);
}

以下は、入力バッファのサイズを知るために「トリック」を使用したというコメントに答えるためのさらに別のバージョンです。実際には、これはトリックではなく、必要な入力知識です(変換するデータのサイズを知る必要があります)。変換コードを別の関数に抽出することで、これをより明確にしました。また、ターゲットバッファの境界チェックコードも追加しました。これは、何をしているのかがわかっている場合は実際には必要ありません。

#include <stdio.h>

void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
    unsigned char * pin = in;
    const char * hex = "0123456789ABCDEF";
    char * pout = out;
    for(; pin < in+insz; pout +=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
        if (pout + 3 - out > outsz){
            /* Better to truncate output string than overflow buffer */
            /* it would be still better to either return a status */
            /* or ensure the target buffer is large enough and it never happen */
            break;
        }
    }
    pout[-1] = 0;
}

int main(){
    enum {insz = 4, outsz = 3*insz};
    unsigned char buf[] = {0, 1, 10, 11};
    char str[outsz];
    tohex(buf, insz, str, outsz);
    printf("%s\n", str);
}

1
これはトリックではなく、単なる定数です。質問のコンテキストでは、16進数に変換するソースの長さがよく知られていることは明らかです(sizeofの代わりにハードコードされた4を配置することもできます)。一般的な場合、関数は既知の長さの入力で呼び出される必要があり、ターゲットバッファには3回+1バイトが使用可能です。これは呼び出し元が確認する必要があります。変換関数がそのタスクを実行する理由はありません。strlen()を呼び出すことは、場合によってはソースサイズを見つける方法かもしれませんが、常にではありません。16進数に変換する数値にゼロが含まれている場合はどうなりますか?
kriss 2015

あなたの関数に触発されて、snprintfなどと同様に出力バッファーに書き込まれたバイト数も返すバージョンを作成しました 。gist.github.com/ Cellularmitosis / 0d8c0abf7f8aa6a2dff3
Jason Pepas 2015

char str [sizeof(buf)* 3 + 1];を使用して、出力バッファを自動的に正しいサイズにする必要があると思います。
セシルワード

また、より多くのconstがあなたを保護します。たとえば、「const unsigned char const * p」を使用すると、入力バッファが書き込まれないようにすることができます。1つはアドレス(または「ポインタ」)を定数または変数にし、もう1つはそのアドレスのメモリを読み取り専用にするかどうかを決定します。多くの場合、ポインタが混同されるのを防ぎます。また、どのバッファーとポインターが入力と出力用であるかを文書化する意味のある名前を持つことも役立ちます。
セシルワード

@Cecil War:私のコードが偽物でない限り、constを使用しても、ポインターを混同したり、入力と出力に同じポインターを使用したりする場合を除いて、あまり保護されません(OK、それでも可能です)。ただし、コンパイラがコードを最適化するのにも役立ちます。さらに良いのは、restrictキーワードも使用することです(C ++ではなくC99が悪すぎますが、コンパイラ拡張として存在することがよくあります)。入力バッファinと出力バッファを呼び出すことに関して、もっと意味のあることは何outですか?また、出力バッファーを提供する代わりに、文字列を使用してコピーを返すことを選択することもできます。最近のC ++オプティマイザーでは、あまり気にしないで十分です。
kriss 2017

15

これがはるかに高速な方法です:

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

unsigned char *     bin_to_strhex(const unsigned char *bin, unsigned int binsz,
                                  unsigned char **result)
{
  unsigned char     hex_str[]= "0123456789abcdef";
  unsigned int      i;

  if (!(*result = (unsigned char *)malloc(binsz * 2 + 1)))
    return (NULL);

  (*result)[binsz * 2] = 0;

  if (!binsz)
    return (NULL);

  for (i = 0; i < binsz; i++)
    {
      (*result)[i * 2 + 0] = hex_str[(bin[i] >> 4) & 0x0F];
      (*result)[i * 2 + 1] = hex_str[(bin[i]     ) & 0x0F];
    }
  return (*result);
}

int                 main()
{
  //the calling
  unsigned char     buf[] = {0,1,10,11};
  unsigned char *   result;

  printf("result : %s\n", bin_to_strhex((unsigned char *)buf, sizeof(buf), &result));
  free(result);

  return 0
}

3
このコードには、印刷できない奇妙な入力でのみ現れるバグが含まれています(数学的に何が起こっているのかを正確に掘り下げる時間がありませんでした)。16進数のバイナリをエンコードしてみてください。ca9e3c972f1c5db40c0b4a66ab5bc1a20ca4457bdbe5e0f8925896d5ed37d726そうすれば出ÌaÌe3cÌ72f1c5dÌ40c0b4a66Ìb5bÌ1Ì20cÌ4457bÌbÌ5Ì0Ì8Ì258Ì6Ì5Ìd37Ì726てきます。これを修正するにhex_strは、forループの最初の行の内側のビットを(input[i] >> 4) & 0x0F@krissの回答のように変更する必要があります。その後、正常に動作します。
niemiro 2014

バグ-malloc()の失敗をチェックしません。
セシルワード

符号付き文字(狂ったDEC PDP11ハードウェア機能)のリスクを誰も望んでいないので、絶対にどこでもunsigned charを使用することをお勧めします。そうすれば、符号付き比較が失敗したり、符号付き右シフトによって値が破損したりするリスクを回避できます。この場合、公平を期すために、コードはどこでも防御的に&0x0Fを実行し、ここであなたを保護します。
セシルワード

このルーチンの目的でメモリを読み取り専用として宣言するには、bin入力パラメータをconst unsigned char const * binにする必要があります。
セシルワード

1
フィードバックに感謝し、Cecil Wardの提案を統合しました
Yannuth 2017年

14

同様の答えがすでに上に存在します。次のコード行が正確にどのように機能するかを説明するために、これを追加しました。

ptr += sprintf(ptr, "%02X", buf[i])

それは静かでトリッキーで理解しにくいです、私は以下のコメントに説明を入れました:

uint8 buf[] = {0, 1, 10, 11};

/* Allocate twice the number of bytes in the "buf" array because each byte would
 * be converted to two hex characters, also add an extra space for the terminating
 * null byte.
 * [size] is the size of the buf array */
char output[(size * 2) + 1];

/* pointer to the first item (0 index) of the output array */
char *ptr = &output[0];

int i;

for (i = 0; i < size; i++) {
    /* "sprintf" converts each byte in the "buf" array into a 2 hex string
     * characters appended with a null byte, for example 10 => "0A\0".
     *
     * This string would then be added to the output array starting from the
     * position pointed at by "ptr". For example if "ptr" is pointing at the 0
     * index then "0A\0" would be written as output[0] = '0', output[1] = 'A' and
     * output[2] = '\0'.
     *
     * "sprintf" returns the number of chars in its output excluding the null
     * byte, in our case this would be 2. So we move the "ptr" location two
     * steps ahead so that the next hex string would be written at the new
     * location, overriding the null byte from the previous hex string.
     *
     * We don't need to add a terminating null byte because it's been already 
     * added for us from the last hex string. */  
    ptr += sprintf(ptr, "%02X", buf[i]);
}

printf("%s\n", output);

素晴らしいロジック。この課題に対するエレガントな非C ++文字列の答えを1時間探していました!
マーク・テリル

6

少しトピックから外れている(標準のCではない)場合でも、以下を追加したかったのですが、頻繁に探していて、最初の検索ヒットの中でこの質問に出くわしました。Linuxカーネルの印刷機能、printk、配列/メモリの内容を単一のフォーマット指定子を介して「直接」出力するためのフォーマット指定子もあります。

https://www.kernel.org/doc/Documentation/printk-formats.txt

Raw buffer as a hex string:
    %*ph    00 01 02  ...  3f
    %*phC   00:01:02: ... :3f
    %*phD   00-01-02- ... -3f
    %*phN   000102 ... 3f

    For printing a small buffers (up to 64 bytes long) as a hex string with
    certain separator. For the larger buffers consider to use
    print_hex_dump(). 

...ただし、これらの形式指定子は、標準のユーザースペースには存在しないようです(s)printf


4

解決

関数は、btox任意のデータ*bbを16進数の終了していない文字列*xpに変換しnます。

void btox(char *xp, const char *bb, int n) 
{
    const char xx[]= "0123456789ABCDEF";
    while (--n >= 0) xp[n] = xx[(bb[n>>1] >> ((1 - (n&1)) << 2)) & 0xF];
}

#include <stdio.h>

typedef unsigned char uint8;

void main(void) 
{
    uint8 buf[] = {0, 1, 10, 11};
    int n = sizeof buf << 1;
    char hexstr[n + 1];

    btox(hexstr, buf, n);
    hexstr[n] = 0; /* Terminate! */
    printf("%s\n", hexstr);
}

結果: 00010A0B

ライブ:Tio.run


1

これは、変換を実行する1つの方法です。

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

#define l_word 15
#define u_word 240

char *hex_str[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};

main(int argc,char *argv[]) {


     char *str = malloc(50);
     char *tmp;
     char *tmp2;

     int i=0;


     while( i < (argc-1)) {
          tmp = hex_str[*(argv[i]) & l_word];
          tmp2 = hex_str[*(argv[i]) & u_word];

          if(i == 0) { memcpy(str,tmp2,1); strcat(str,tmp);}
          else { strcat(str,tmp2); strcat(str,tmp);}
          i++;
    }

    printf("\n*********  %s  *************** \n", str);

}

1

わずかに変更されたYannithバージョン。戻り値として欲しいだけです

typedef struct {
   size_t len;
   uint8_t *bytes;
} vdata;

char* vdata_get_hex(const vdata data)
{
   char hex_str[]= "0123456789abcdef";

   char* out;
   out = (char *)malloc(data.len * 2 + 1);
   (out)[data.len * 2] = 0;
   
   if (!data.len) return NULL;
   
   for (size_t i = 0; i < data.len; i++) {
      (out)[i * 2 + 0] = hex_str[(data.bytes[i] >> 4) & 0x0F];
      (out)[i * 2 + 1] = hex_str[(data.bytes[i]     ) & 0x0F];
   }
   return out;
}


1

この関数は、ユーザー/呼び出し元が16進文字列を文字配列/バッファーに入れたい場合に適しています。文字バッファに16進文字列を使用すると、ユーザー/呼び出し元は独自のマクロ/関数を使用して、任意の場所(ファイルなど)に表示またはログを記録できます。この関数を使用すると、呼び出し元は各行に入れる(16進)バイト数を制御することもできます。

/**
 * @fn 
 * get_hex
 *
 * @brief 
 * Converts a char into bunary string 
 *
 * @param[in]   
 *     buf Value to be converted to hex string
 * @param[in]   
 *     buf_len Length of the buffer
 * @param[in]   
 *     hex_ Pointer to space to put Hex string into
 * @param[in]   
 *     hex_len Length of the hex string space
 * @param[in]   
 *     num_col Number of columns in display hex string
 * @param[out]   
 *     hex_ Contains the hex string
 * @return  void
 */
static inline void
get_hex(char *buf, int buf_len, char* hex_, int hex_len, int num_col)
{
    int i;
#define ONE_BYTE_HEX_STRING_SIZE   3
  unsigned int byte_no = 0;

  if (buf_len <= 0) {
      if (hex_len > 0) {
        hex_[0] = '\0';
      }
      return;
  }

  if(hex_len < ONE_BYTE_HEX_STRING_SIZE + 1)
  {
      return;
  }

  do {
         for (i = 0; ((i < num_col) && (buf_len > 0) && (hex_len > 0)); ++i )
         {
            snprintf(hex_, hex_len, "%02X ", buf[byte_no++] & 0xff);
            hex_ += ONE_BYTE_HEX_STRING_SIZE;
            hex_len -=ONE_BYTE_HEX_STRING_SIZE;
            buf_len--;
         }
         if (buf_len > 1)
         {
             snprintf(hex_, hex_len, "\n");
             hex_ += 1;
         }
  } while ((buf_len) > 0 && (hex_len > 0));

}

例:コード

#define DATA_HEX_STR_LEN 5000
    char      data_hex_str[DATA_HEX_STR_LEN];

    get_hex(pkt, pkt_len, data_hex_str, DATA_HEX_STR_LEN, 16);
    //      ^^^^^^^^^^^^                                  ^^
    //      Input byte array                              Number of (hex) byte
    //      to be converted to hex string                 columns in hex string

    printf("pkt:\n%s",data_hex_str) 

出力

pkt:
BB 31 32 00 00 00 00 00 FF FF FF FF FF FF DE E5 
A8 E2 8E C1 08 06 00 01 08 00 06 04 00 01 DE E5 
A8 E2 8E C1 67 1E 5A 02 00 00 00 00 00 00 67 1E 
5A 01 

0

Cには、これに対するプリミティブはありません。おそらく、十分な長さのバッファをmalloc(またはalloca)して、入力をループします。また、C ++と同様のセマンティクス(構文ではありません!)を備えた動的文字列ライブラリで行われることも確認しました。ostringstreamこれは、おそらくより一般的なソリューションですが、1つのケースだけでさらに複雑にする価値はないかもしれません。


0

16進値をchar *文字列に格納する場合は、を使用できますsnprintf。先頭のゼロとコロンを含む、印刷されるすべての文字にスペースを割り当てる必要があります。

マークの答えを拡張する:

char str_buf* = malloc(3*X + 1);   // X is the number of bytes to be converted

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) snprintf(str_buf, 1, ":");
    snprintf(str_buf, 2, "%02X", num_buf[i]);  // need 2 characters for a single hex value
}
snprintf(str_buf, 2, "\n\0"); // dont forget the NULL byte

したがってstr_buf、16進文字列が含まれます。


これにより、最初の2文字が何度も上書きされます。
xordon 2014

0

コロン区切り文字を含めるように適合されたZincXのソリューション:

char buf[] = {0,1,10,11};
int i, size = sizeof(buf) / sizeof(char);
char *buf_str = (char*) malloc(3 * size), *buf_ptr = buf_str;
if (buf_str) {
  for (i = 0; i < size; i++)
    buf_ptr += sprintf(buf_ptr, i < size - 1 ? "%02X:" : "%02X\0", buf[i]);
  printf("%s\n", buf_str);
  free(buf_str);
}

0

興味のある人のために、ここにC ++バージョンを追加します。

#include <iostream>
#include <iomanip>
inline void print_bytes(char const * buffer, std::size_t count, std::size_t bytes_per_line, std::ostream & out) {
    std::ios::fmtflags flags(out.flags()); // Save flags before manipulation.
    out << std::hex << std::setfill('0');
    out.setf(std::ios::uppercase);
    for (std::size_t i = 0; i != count; ++i) {
        auto current_byte_number = static_cast<unsigned int>(static_cast<unsigned char>(buffer[i]));
        out << std::setw(2) << current_byte_number;
        bool is_end_of_line = (bytes_per_line != 0) && ((i + 1 == count) || ((i + 1) % bytes_per_line == 0));
        out << (is_end_of_line ? '\n' : ' ');
    }
    out.flush();
    out.flags(flags); // Restore original flags.
}

それはのhexdumpが印刷されるbuffer長さのをcountするためにstd::ostream out(あなたはそれがデフォルトにすることができますstd::cout)。すべての行にはbytes_per_lineバイトが含まれ、各バイトは大文字の2桁の16進数を使用して表されます。バイト間にスペースがあります。そして、行の終わりまたはバッファの終わりに、改行を出力します。bytes_per_lineが0に設定されている場合、new_lineは出力されません。自分で試してみてください。


0

簡単に使用するために、入力文字列(バイナリデータ)をエンコードする関数を作成しました。

/* Encodes string to hexadecimal string reprsentation
    Allocates a new memory for supplied lpszOut that needs to be deleted after use
    Fills the supplied lpszOut with hexadecimal representation of the input
    */
void StringToHex(unsigned char *szInput, size_t size_szInput, char **lpszOut)
{
    unsigned char *pin = szInput;
    const char *hex = "0123456789ABCDEF";
    size_t outSize = size_szInput * 2 + 2;
    *lpszOut = new char[outSize];
    char *pout = *lpszOut;
    for (; pin < szInput + size_szInput; pout += 2, pin++)
    {
        pout[0] = hex[(*pin >> 4) & 0xF];
        pout[1] = hex[*pin & 0xF];
    }
    pout[0] = 0;
}

使用法:

unsigned char input[] = "This is a very long string that I want to encode";
char *szHexEncoded = NULL;
StringToHex(input, strlen((const char *)input), &szHexEncoded);

printf(szHexEncoded);

// The allocated memory needs to be deleted after usage
delete[] szHexEncoded;

0

Yannuthの回答に基づいていますが、簡略化されています。

ここで、の長さはのdest[]2倍であることを意味しlen、その割り当ては呼び出し元によって管理されます。

void create_hex_string_implied(const unsigned char *src, size_t len, unsigned char *dest)
{
    static const unsigned char table[] = "0123456789abcdef";

    for (; len > 0; --len)
    {
        unsigned char c = *src++;
        *dest++ = table[c >> 4];
        *dest++ = table[c & 0x0f];
    }
}

0

私はこの質問にすでに答えがあることを知っていますが、私の解決策は誰かを助けることができると思います。

したがって、私の場合、キーを表すバイト配列があり、このバイト配列を1行で出力するために16進値のchar配列に変換する必要がありました。コードを次のような関数に抽出しました。

char const * keyToStr(uint8_t const *key)
{
    uint8_t offset = 0;
    static char keyStr[2 * KEY_SIZE + 1];

    for (size_t i = 0; i < KEY_SIZE; i++)
    {
        offset += sprintf(keyStr + offset, "%02X", key[i]);
    }
    sprintf(keyStr + offset, "%c", '\0');

    return keyStr;
}

これで、次のように関数を使用できます。

Serial.print("Public key: ");
Serial.println(keyToStr(m_publicKey));

SerialオブジェクトはArduinoライブラリの一部でありm_publicKey、次の宣言を持つ私のクラスのメンバーですuint8_t m_publicKey[32]


0

snprintfとmallocで解決できます。

char c_buff[50];

u8_number_val[] = { 0xbb, 0xcc, 0xdd, 0x0f, 0xef, 0x0f, 0x0e, 0x0d, 0x0c };

char *s_temp = malloc(u8_size * 2 + 1);

for (uint8_t i = 0; i < u8_size; i++)
{
    snprintf(s_temp  + i * 2, 3, "%02x", u8_number_val[i]);
}

snprintf(c_buff, strlen(s_temp)+1, "%s", s_temp );

printf("%s\n",c_buff);

free(s);

OUT:bbccdd0fef0f0e0d0c


-2

なんて複雑な解決策でしょう。
マロックとスプリントとキャストオーマイ。(OZ引用)
そしてどこにも単一のレムではありません。まあ

どのようにこのようなものでしょうか?

main()
{
    // the value
    int value = 16;

    // create a string array with a '\0' ending ie. 0,0,0
    char hex[]= {0,0,'\0'}; 
    char *hex_p=hex;

    //a working variable
    int TEMP_int=0;

    // get me how many 16s are in this code
    TEMP_int=value/16;

    // load the first character up with 
    // 48+0 gives you ascii 0, 55+10 gives you ascii A
    if (TEMP_int<10) {*hex_p=48+TEMP_int;}
        else {*hex_p=55+TEMP_int;}

    // move that pointer to the next (less significant byte)<BR>
    hex_p++;

    // get me the remainder after I have divied by 16
    TEMP_int=value%16;

    // 48+0 gives you ascii 0, 55+10 gives you ascii A
    if (TEMP_int<10) {*hex_p=48+TEMP_int;}
        else {*hex_p=55+TEMP_int;}

    // print the result
    printf("%i , 0x%s",value,hex);

}

これで、16進数が2桁になりました。区切り文字を追加し、変換する他のバイトを処理する必要があります。多分ループで?それを関数にすると、私のようなものになります(ただし、冗長で読みにくいものになります)。たぶん、他のポスターに名前を呼ぶ前に、少なくとも仕事を終えるべきですか?
kriss 2015年

1
そして、ソースコード内のコメントについての一言(REMではなく、コメントのBASICキーワードです。それは避けてください):コードが何をしているのかを英語で言うコメントは非常に悪い習慣です!はい、プログラマーはモジュロ演算子が何を意味するかを知っているはずです(残りを与えます)そしてその除算は数が別のものに現れる回数を数えます...そしてそのprintfは結果を印刷します。オーマイ!
kriss 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.