Cの文字列から部分文字列を置き換える関数は何ですか?


94

char *)文字列が与えられた場合、サブ文字列のすべての出現を検索し、それらを代替文字列で置き換えたいと思います。これを実現する単純な関数はにはありません<string.h>


4
変更可能な方法でこれを行うことができるとは思いません
user44511 2009

回答:


87

オプティマイザは、ローカル変数のほとんどを排除する必要があります。tmpポインタは、strcpyがnullを見つけるために文字列を歩く必要がないことを確認するためにあります。tmpは、各呼び出し後の結果の終わりを指します。(strcpyが煩わしい理由については、画家のアルゴリズムであるShlemielを参照してください。)

// You must free the result if result is non-NULL.
char *str_replace(char *orig, char *rep, char *with) {
    char *result; // the return string
    char *ins;    // the next insert point
    char *tmp;    // varies
    int len_rep;  // length of rep (the string to remove)
    int len_with; // length of with (the string to replace rep with)
    int len_front; // distance between rep and end of last rep
    int count;    // number of replacements

    // sanity checks and initialization
    if (!orig || !rep)
        return NULL;
    len_rep = strlen(rep);
    if (len_rep == 0)
        return NULL; // empty rep causes infinite loop during count
    if (!with)
        with = "";
    len_with = strlen(with);

    // count the number of replacements needed
    ins = orig;
    for (count = 0; tmp = strstr(ins, rep); ++count) {
        ins = tmp + len_rep;
    }

    tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);

    if (!result)
        return NULL;

    // first time through the loop, all the variable are set correctly
    // from here on,
    //    tmp points to the end of the result string
    //    ins points to the next occurrence of rep in orig
    //    orig points to the remainder of orig after "end of rep"
    while (count--) {
        ins = strstr(orig, rep);
        len_front = ins - orig;
        tmp = strncpy(tmp, orig, len_front) + len_front;
        tmp = strcpy(tmp, with) + len_with;
        orig += len_front + len_rep; // move to next "end of rep"
    }
    strcpy(tmp, orig);
    return result;
}

@jmucchiello:任意のオブジェクト/文字列サイズとそれらへのインデックスのsize_t代わりに使用intします。また、strcpy(tmp, orig);最後の目的は何ですか?それは間違っているようです。
Alexey Frunze

@Alex、最後のstrcpy(tmp、orig)は、stringの最後の部分を宛先にコピーします。例:ループの最後のreplace( "abab"、 "a"、 "c")、結果には "cbc"が含まれ、origは "abab"の最後の "b"を指します。最後のstrcpyは「b」を追加するため、返される文字列は「cbcb」です。コピーするものが残っていない場合、origは入力文字列のASCIIZを指している必要があります。
jmucchiello

簡略化:最初のforループをに置き換えるとfor (count = 1; ins = strstr(ins + rep_len, rep); ++count) {}tmp書き込みにのみ使用されます。
ラピオン2013年

1
char * done = replace( "abcdefghijkl"、 "bc"、 "yz"); do_stuff(); 無料(完了);
jmucchiello 2013年

2
置換するオカレンスがない場合、この関数はNULLを返すことに注意してください(if(!(ins = strstr(orig、rep)))return NULL;)。出力を使用するだけでなく、出力がNULLかどうかを確認し、そうである場合は元の文字列を使用する必要があります(free(result)が元の文字列を解放するため、ポインタを結果文字列にコピーしないでください)。置換するものが何もない場合に入力文字列を出力文字列にコピーするだけの方が、使い方は簡単です。
Adversus 2013年

18

これは標準のCライブラリでは提供されていません。char*のみを指定すると、置換される文字列が置換される文字列よりも長い場合、文字列に割り当てられたメモリを増やすことができないためです。

std :: stringを使用すると、これを簡単に行うことができますが、それでも、単一の関数がそれを行うことはありません。


12
この質問は、C ++ではなくCに関するものです。
ジェレミア

1 / strlen(char *)+ 1は、必ずしもストレージサイズと同じではありません。2 /バッファサイズパラメータを受け取って追加する文字列関数のNバージョンはたくさんあるので、snreplace()ができなかった理由はありません。3 /インプレース置換機能があり、インプレース置換機能がない場合があります。4 / sprintfはどのように機能すると思いますか?char *引数が与えられ、メモリ割り当てを増やす必要がないので、置換も機能しなかった理由はありません...ポインタ=> snprintf)
Steven Spark

12

ありません。

strstrやstrcatやstrcpyのようなものを使って自分でロールする必要があります。


7
よく使用される機能のファンコレクションはどこに保存されますか?確かに....すでにそのためのライブラリがあります
Pacerier

1
strcat()悪い提案です。
Iharob Al Asimi、2015

11

strstrを使用して独自の置換関数を作成し、部分文字列を検索して、strncpyを部分的に新しいバッファーにコピーできます。

必要なreplace_with長さが必要な長さと同じでない限りreplace、新しいバッファを使用して新しい文字列をコピーすることをお勧めします。


9

Cの文字列はインプレース置換を動的に拡張できないため、通常は機能しません。したがって、置換のための十分なスペースがある新しい文字列にスペースを割り当て、元の部分と置換部分を新しい文字列にコピーする必要があります。パーツをコピーするには、strncpyを使用します。


バッファサイズはstrlenよりも大きく、置換文字列は置換後の文字列よりも小さい可能性があります。したがって、置換を実行するためにメモリを割り当てる必要はありません。(また、マイクロコントローラーでは、メモリが無限でなく、代わりに置換を実行する必要があるかもしれません。すべてを新しいバッファーにコピーすることは、すべての人にとって適切なソリューションではないかもしれません...)
Steven Spark

8

これを行うサンプルコードを次に示します。

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

char * replace(
    char const * const original, 
    char const * const pattern, 
    char const * const replacement
) {
  size_t const replen = strlen(replacement);
  size_t const patlen = strlen(pattern);
  size_t const orilen = strlen(original);

  size_t patcnt = 0;
  const char * oriptr;
  const char * patloc;

  // find how many times the pattern occurs in the original string
  for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen)
  {
    patcnt++;
  }

  {
    // allocate memory for the new string
    size_t const retlen = orilen + patcnt * (replen - patlen);
    char * const returned = (char *) malloc( sizeof(char) * (retlen + 1) );

    if (returned != NULL)
    {
      // copy the original string, 
      // replacing all the instances of the pattern
      char * retptr = returned;
      for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen)
      {
        size_t const skplen = patloc - oriptr;
        // copy the section until the occurence of the pattern
        strncpy(retptr, oriptr, skplen);
        retptr += skplen;
        // copy the replacement 
        strncpy(retptr, replacement, replen);
        retptr += replen;
      }
      // copy the rest of the string.
      strcpy(retptr, oriptr);
    }
    return returned;
  }
}

#include <stdio.h>
int main(int argc, char * argv[])
{
  if (argc != 4)
  {
    fprintf(stderr,"usage: %s <original text> <pattern> <replacement>\n", argv[0]);
    exit(-1);
  }
  else
  {
    char * const newstr = replace(argv[1], argv[2], argv[3]);
    if (newstr)
    {
      printf("%s\n", newstr);
      free(newstr);
    }
    else
    {
      fprintf(stderr,"allocation error\n");
      exit(-2);
    }
  }
  return 0;
}

動作しますが、少しバグがありますが、とにかくありがとう!:Dここに私が非常にうまく機能することがわかったものがあります。coding.debuntu.org/…乾杯!:)
Joe DF

4
// Here is the code for unicode strings!


int mystrstr(wchar_t *txt1,wchar_t *txt2)
{
    wchar_t *posstr=wcsstr(txt1,txt2);
    if(posstr!=NULL)
    {
        return (posstr-txt1);
    }else
    {
        return -1;
    }
}

// assume: supplied buff is enough to hold generated text
void StringReplace(wchar_t *buff,wchar_t *txt1,wchar_t *txt2)
{
    wchar_t *tmp;
    wchar_t *nextStr;
    int pos;

    tmp=wcsdup(buff);

    pos=mystrstr(tmp,txt1);
    if(pos!=-1)
    {
        buff[0]=0;
        wcsncpy(buff,tmp,pos);
        buff[pos]=0;

        wcscat(buff,txt2);

        nextStr=tmp+pos+wcslen(txt1);

        while(wcslen(nextStr)!=0)
        {
            pos=mystrstr(nextStr,txt1);

            if(pos==-1)
            {
                wcscat(buff,nextStr);
                break;
            }

            wcsncat(buff,nextStr,pos);
            wcscat(buff,txt2);

            nextStr=nextStr+pos+wcslen(txt1);   
        }
    }

    free(tmp);
}

3

repl_str() creativeandcritical.net上の機能は、高速かつ信頼性の高いです。このページには、ワイド文字列バリアントrepl_wcs()も含まれています。これは、ヘルパー関数を介して、UTF-8でエンコードされた文字列を含むUnicode文字列で使用できます-デモコードはページからリンクされます。遅ればせながら完全に公開:私はそのページとその機能の作成者です。


3
高速で信頼性が高いが、メモリリークが非常に大きい。
MightyPork 2014年

3
どうすればいいのかわかりません。mallocは1つしかなく、呼び出し側は、メモリが不要になったときにメモリを解放するように指示されます。もっと具体的に教えていただけますか?
Laird

@Lairdpos_cache = realloc(pos_cache
PSkocik

@PSkocik関数は@MightyPorkからの苦情以来アップグレードされていますが、pos_cacheにmalloc / reallocが追加されているにもかかわらずfree(pos_cache);、関数の終わりを回避するコードパスが表示されません。
Laird

@Laird reallocは失敗する可能性があります。存在する場合、戻りNULL、古いポインタはそのまま残します。p = realloc(p, x)失敗した場合は、有効なヒープポインタpをで書き換えます。NULLそれpがそのヒープオブジェクトへの唯一の参照である場合は、リークされています。それは古典的な初心者の間違いです。
PSkocik 2016

3

私は提案された関数のほとんどを理解するのが難しいと思います-それで私はこれを思いつきました:

static char *dull_replace(const char *in, const char *pattern, const char *by)
{
    size_t outsize = strlen(in) + 1;
    // TODO maybe avoid reallocing by counting the non-overlapping occurences of pattern
    char *res = malloc(outsize);
    // use this to iterate over the output
    size_t resoffset = 0;

    char *needle;
    while (needle = strstr(in, pattern)) {
        // copy everything up to the pattern
        memcpy(res + resoffset, in, needle - in);
        resoffset += needle - in;

        // skip the pattern in the input-string
        in = needle + strlen(pattern);

        // adjust space for replacement
        outsize = outsize - strlen(pattern) + strlen(by);
        res = realloc(res, outsize);

        // copy the pattern
        memcpy(res + resoffset, by, strlen(by));
        resoffset += strlen(by);
    }

    // copy the remaining input
    strcpy(res + resoffset, in);

    return res;
}

出力を解放する必要があります


2

この関数を使用できます(コメントはどのように機能するかを説明しています):

void strreplace(char *string, const char *find, const char *replaceWith){
    if(strstr(string, replaceWith) != NULL){
        char *temporaryString = malloc(strlen(strstr(string, find) + strlen(find)) + 1);
        strcpy(temporaryString, strstr(string, find) + strlen(find));    //Create a string with what's after the replaced part
        *strstr(string, find) = '\0';    //Take away the part to replace and the part after it in the initial string
        strcat(string, replaceWith);    //Concat the first part of the string with the part to replace with
        strcat(string, temporaryString);    //Concat the first part of the string with the part after the replaced part
        free(temporaryString);    //Free the memory to avoid memory leaks
    }
}

1

以下は、これらの要件に基づいて作成したものです。

  1. が長いか短いかに関係なく、パターンを置き換えます。

  2. 本質的にメモリリークを回避するために、malloc(明示的または暗黙的)を使用しないでください。

  3. 任意の数のパターンの出現を置き換えます。

  4. 検索文字列と等しい部分文字列を持つ置換文字列を許容します。

  5. Line配列が置換を保持するのに十分なサイズであることを確認する必要はありません。たとえば、行が新しい文字列を保持するのに十分なサイズであることを呼び出し元が知らない限り、これは機能しません。

/* returns number of strings replaced.
*/
int replacestr(char *line, const char *search, const char *replace)
{
   int count;
   char *sp; // start of pattern

   //printf("replacestr(%s, %s, %s)\n", line, search, replace);
   if ((sp = strstr(line, search)) == NULL) {
      return(0);
   }
   count = 1;
   int sLen = strlen(search);
   int rLen = strlen(replace);
   if (sLen > rLen) {
      // move from right to left
      char *src = sp + sLen;
      char *dst = sp + rLen;
      while((*dst = *src) != '\0') { dst++; src++; }
   } else if (sLen < rLen) {
      // move from left to right
      int tLen = strlen(sp) - sLen;
      char *stop = sp + rLen;
      char *src = sp + sLen + tLen;
      char *dst = sp + rLen + tLen;
      while(dst >= stop) { *dst = *src; dst--; src--; }
   }
   memcpy(sp, replace, rLen);

   count += replacestr(sp + rLen, search, replace);

   return(count);
}

このコードを改善するための提案は陽気に受け入れられます。コメントを投稿するだけで、テストします。


1

あなたはstrrep()を使うことができます

char * strrep(const char * cadena、const char * strf、const char * strr)

strrep(文字列置換)。「cadena」で「strf」を「strr」に置き換え、新しい文字列を返します。strrepを使用した後、コードで返された文字列を解放する必要があります。

パラメータcadenaテキストを含む文字列。strf検索するテキスト。strr置換テキスト。

戻り値置換により更新されたテキスト。

プロジェクトはhttps://github.com/ipserc/strrepにあります


0

文字列のインプレース変更を使用し、lineが指すバッファーが結果の文字列を保持するのに十分な大きさであると想定して、fann95の応答を修正しました。

static void replacestr(char *line, const char *search, const char *replace)
{
     char *sp;

     if ((sp = strstr(line, search)) == NULL) {
         return;
     }
     int search_len = strlen(search);
     int replace_len = strlen(replace);
     int tail_len = strlen(sp+search_len);

     memmove(sp+replace_len,sp+search_len,tail_len+1);
     memcpy(sp, replace, replace_len);
}

0

あなたが行くあり....これは、すべてののoccurance交換する機能であるchar xchar y、文字列の中には、str

char *zStrrep(char *str, char x, char y){
    char *tmp=str;
    while(*tmp)
        if(*tmp == x)
            *tmp++ = y; /* assign first, then incement */
        else
            *tmp++;

    *tmp='\0';
    return str;
}

使用例は

  Exmaple Usage
        char s[]="this is a trial string to test the function.";
        char x=' ', y='_';
        printf("%s\n",zStrrep(s,x,y));

  Example Output
        this_is_a_trial_string_to_test_the_function.

関数は、私がGithub管理している文字列ライブラリからのものです。他の使用可能な関数を確認したり、コードに貢献したりすることもできます。

https://github.com/fnoyanisi/zString

編集:@sirideは正しい、上記の関数は文字のみを置き換えます。文字列を置き換えるこれを書いただけです。

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

/* replace every occurance of string x with string y */
char *zstring_replace_str(char *str, const char *x, const char *y){
    char *tmp_str = str, *tmp_x = x, *dummy_ptr = tmp_x, *tmp_y = y;
    int len_str=0, len_y=0, len_x=0;

    /* string length */
    for(; *tmp_y; ++len_y, ++tmp_y)
        ;

    for(; *tmp_str; ++len_str, ++tmp_str)
        ;

    for(; *tmp_x; ++len_x, ++tmp_x)
        ;

    /* Bounds check */
    if (len_y >= len_str)
        return str;

    /* reset tmp pointers */
    tmp_y = y;
    tmp_x = x;

    for (tmp_str = str ; *tmp_str; ++tmp_str)
        if(*tmp_str == *tmp_x) {
            /* save tmp_str */
            for (dummy_ptr=tmp_str; *dummy_ptr == *tmp_x; ++tmp_x, ++dummy_ptr)
                if (*(tmp_x+1) == '\0' && ((dummy_ptr-str+len_y) < len_str)){
                /* Reached end of x, we got something to replace then!
                * Copy y only if there is enough room for it
                */
                    for(tmp_y=y; *tmp_y; ++tmp_y, ++tmp_str)
                        *tmp_str = *tmp_y;
            }
        /* reset tmp_x */
        tmp_x = x;
        }

    return str;
}

int main()
{
    char s[]="Free software is a matter of liberty, not price.\n"
             "To understand the concept, you should think of 'free' \n"
             "as in 'free speech', not as in 'free beer'";

    printf("%s\n\n",s);
    printf("%s\n",zstring_replace_str(s,"ree","XYZ"));
    return 0;
}

そして以下は出力です

Free software is a matter of liberty, not price.
To understand the concept, you should think of 'free' 
as in 'free speech', not as in 'free beer'

FXYZ software is a matter of liberty, not price.
To understand the concept, you should think of 'fXYZ' 
as in 'fXYZ speech', not as in 'fXYZ beer'

これは、部分文字列ではなく単一の文字のみを置き換えます。
siride

0
/*замена символа в строке*/
char* replace_char(char* str, char in, char out) {
    char * p = str;

    while(p != '\0') {
        if(*p == in)
            *p == out;
        ++p;
    }

    return str;
}

strがnullの場合のsegfault
Code_So1dier

0
DWORD ReplaceString(__inout PCHAR source, __in DWORD dwSourceLen, __in const char* pszTextToReplace, __in const char* pszReplaceWith)
{
    DWORD dwRC = NO_ERROR;
    PCHAR foundSeq = NULL;
    PCHAR restOfString = NULL;
    PCHAR searchStart = source;
    size_t szReplStrcLen = strlen(pszReplaceWith), szRestOfStringLen = 0, sztextToReplaceLen = strlen(pszTextToReplace), remainingSpace = 0, dwSpaceRequired = 0;
    if (strcmp(pszTextToReplace, "") == 0)
        dwRC = ERROR_INVALID_PARAMETER;
    else if (strcmp(pszTextToReplace, pszReplaceWith) != 0)
    {
        do
        {
            foundSeq = strstr(searchStart, pszTextToReplace);
            if (foundSeq)
            {
                szRestOfStringLen = (strlen(foundSeq) - sztextToReplaceLen) + 1;
                remainingSpace = dwSourceLen - (foundSeq - source);
                dwSpaceRequired = szReplStrcLen + (szRestOfStringLen);
                if (dwSpaceRequired > remainingSpace)
                {
                    dwRC = ERROR_MORE_DATA;
                }

                else
                {
                    restOfString = CMNUTIL_calloc(szRestOfStringLen, sizeof(CHAR));
                    strcpy_s(restOfString, szRestOfStringLen, foundSeq + sztextToReplaceLen);

                    strcpy_s(foundSeq, remainingSpace, pszReplaceWith);
                    strcat_s(foundSeq, remainingSpace, restOfString);
                }

                CMNUTIL_free(restOfString);
                searchStart = foundSeq + szReplStrcLen; //search in the remaining str. (avoid loops when replWith contains textToRepl 
            }
        } while (foundSeq && dwRC == NO_ERROR);
    }
    return dwRC;
}

0
char *replace(const char*instring, const char *old_part, const char *new_part)
{

#ifndef EXPECTED_REPLACEMENTS
    #define EXPECTED_REPLACEMENTS 100
#endif

    if(!instring || !old_part || !new_part)
    {
        return (char*)NULL;
    }

    size_t instring_len=strlen(instring);
    size_t new_len=strlen(new_part);
    size_t old_len=strlen(old_part);
    if(instring_len<old_len || old_len==0)
    {
        return (char*)NULL;
    }

    const char *in=instring;
    const char *found=NULL;
    size_t count=0;
    size_t out=0;
    size_t ax=0;
    char *outstring=NULL;

    if(new_len> old_len )
    {
        size_t Diff=EXPECTED_REPLACEMENTS*(new_len-old_len);
        size_t outstring_len=instring_len + Diff;
        outstring =(char*) malloc(outstring_len); 
        if(!outstring){
            return (char*)NULL;
        }
        while((found = strstr(in, old_part))!=NULL)
        {
            if(count==EXPECTED_REPLACEMENTS)
            {
                outstring_len+=Diff;
                if((outstring=realloc(outstring,outstring_len))==NULL)
                {
                     return (char*)NULL;
                }
                count=0;
            }
            ax=found-in;
            strncpy(outstring+out,in,ax);
            out+=ax;
            strncpy(outstring+out,new_part,new_len);
            out+=new_len;
            in=found+old_len;
            count++;
        }
    }
    else
    {
        outstring =(char*) malloc(instring_len);
        if(!outstring){
            return (char*)NULL;
        }
        while((found = strstr(in, old_part))!=NULL)
        {
            ax=found-in;
            strncpy(outstring+out,in,ax);
            out+=ax;
            strncpy(outstring+out,new_part,new_len);
            out+=new_len;
            in=found+old_len;
        }
    }
    ax=(instring+instring_len)-in;
    strncpy(outstring+out,in,ax);
    out+=ax;
    outstring[out]='\0';

    return outstring;
}

0

この関数は、文字列に新しい長さの余分なスペースがある場合にのみ機能します

void replace_str(char *str,char *org,char *rep)
{
    char *ToRep = strstr(str,org);
    char *Rest = (char*)malloc(strlen(ToRep));
    strcpy(Rest,((ToRep)+strlen(org)));

    strcpy(ToRep,rep);
    strcat(ToRep,Rest);

    free(Rest);
}

これは最初の発生のみを置き換えます


0

これは私のものです、それは自己完結型で多目的であり、効率的です、それは各再帰で必要に応じてバッファを拡大または縮小します

void strreplace(char *src, char *str, char *rep)
{
    char *p = strstr(src, str);
    if (p)
    {
        int len = strlen(src)+strlen(rep)-strlen(str);
        char r[len];
        memset(r, 0, len);
        if ( p >= src ){
            strncpy(r, src, p-src);
            r[p-src]='\0';
            strncat(r, rep, strlen(rep));
            strncat(r, p+strlen(str), p+strlen(str)-src+strlen(src));
            strcpy(src, r);
            strreplace(p+strlen(rep), str, rep);
        }
    }
}

0

これが私のものです、それらをすべてchar *にすることで、呼び出しが簡単になります...

char *strrpc(char *str,char *oldstr,char *newstr){
    char bstr[strlen(str)];
    memset(bstr,0,sizeof(bstr));
    int i;
    for(i = 0;i < strlen(str);i++){
        if(!strncmp(str+i,oldstr,strlen(oldstr))){
            strcat(bstr,newstr);
            i += strlen(oldstr) - 1;
        }else{
                strncat(bstr,str + i,1);
            }
    }

    strcpy(str,bstr);
    return str;
}

0

string.hからstrlenのみを使用する

私の英語でごめんなさい

char * str_replace(char * text,char * rep, char * repw){//text -> to replace in it | rep -> replace | repw -> replace with
    int replen = strlen(rep),repwlen = strlen(repw),count;//some constant variables
    for(int i=0;i<strlen(text);i++){//search for the first character from rep in text
        if(text[i] == rep[0]){//if it found it
            count = 1;//start searching from the next character to avoid repetition
            for(int j=1;j<replen;j++){
                if(text[i+j] == rep[j]){//see if the next character in text is the same as the next in the rep if not break
                    count++;
                }else{
                    break;
                }
            }
            if(count == replen){//if count equals to the lenght of the rep then we found the word that we want to replace in the text
                if(replen < repwlen){
                    for(int l = strlen(text);l>i;l--){//cuz repwlen greater than replen we need to shift characters to the right to make space for the replacement to fit
                        text[l+repwlen-replen] = text[l];//shift by repwlen-replen
                    }
                }
                if(replen > repwlen){
                    for(int l=i+replen-repwlen;l<strlen(text);l++){//cuz replen greater than repwlen we need to shift the characters to the left
                        text[l-(replen-repwlen)] = text[l];//shift by replen-repwlen
                    }
                    text[strlen(text)-(replen-repwlen)] = '\0';//get rid of the last unwanted characters
                }
                for(int l=0;l<repwlen;l++){//replace rep with repwlen
                    text[i+l] = repw[l];
                }
                if(replen != repwlen){
                    i+=repwlen-1;//pass to the next character | try text "y" ,rep "y",repw "yy" without this line to understand
                }
            }
        }
    }
    return text;
}

strlenコードでstring.hを呼び出さないようにする場合

int strlen(char * string){//use this code to avoid calling string.h
    int lenght = 0;
    while(string[lenght] != '\0'){
        lenght++;
    }
    return lenght;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.