Cでconst / literal文字列を連結するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/308695

  •  08-07-2019
  •  | 
  •  

質問

Cで作業していますが、いくつかのことを連結する必要があります。

今、私はこれを持っています:

message = strcat("TEXT ", var);

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

Cの経験があれば、これを実行しようとするとセグメンテーションエラーが発生することに気づいたと思います。それでどうすればそれを回避できますか?

役に立ちましたか?

解決

Cでは、<!> quot; strings <!> quot;単なるプレーンなchar配列です。したがって、それらを他の<!> quot; strings <!> quot;と直接連結することはできません。

strcat関数を使用できます。この関数は、srcが指す文字列を、destが指す文字列の末尾に追加します。

char *strcat(char *dest, const char *src);

cplusplus.comの例

char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");

最初のパラメーターには、宛先バッファー自体を指定する必要があります。宛先バッファーは、char配列バッファーでなければなりません。例:char buffer[1024];

最初のパラメータに、コピーしようとしているものを保存するのに十分なスペースがあることを確認してください。使用可能な場合は、宛先バッファのサイズを明示的に指定する必要があるstrcpy_sおよびstrcat_sなどの関数を使用する方が安全です。

:文字列リテラルは定数であるため、バッファーとして使用できません。したがって、常にバッファにchar配列を割り当てる必要があります。

<=>の戻り値は単純に無視でき、最初の引数として渡されたものと同じポインタを返すだけです。利便性のためにあり、呼び出しを1行のコードにチェーンできます。

strcat(strcat(str, foo), bar);

したがって、問題は次のように解決できます。

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);

他のヒント

フォーク、str n cpy()、str n cat()、またはs n printf()を使用します。
バッファスペースを超えると、メモリ内の後続のものがすべて破棄されます。
(また、末尾のヌル「\ 0」文字にスペースを入れることを忘れないでください!)

連結される文字列の数が事前にわからない場合にも、mallocとreallocは便利です。

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

void example(const char *header, const char **words, size_t num_words)
{
    size_t message_len = strlen(header) + 1; /* + 1 for terminating NULL */
    char *message = (char*) malloc(message_len);
    strncat(message, header, message_len);

    for(int i = 0; i < num_words; ++i)
    {
       message_len += 1 + strlen(words[i]); /* 1 + for separator ';' */
       message = (char*) realloc(message, message_len);
       strncat(strncat(message, ";", message_len), words[i], message_len);
    }

    puts(message);

    free(message);
}

文字列は、コンパイル時に連結することもできます。

#define SCHEMA "test"
#define TABLE  "data"

const char *table = SCHEMA "." TABLE ; // note no + or . or anything
const char *qry =               // include comments in a string
    " SELECT * "                // get all fields
    " FROM " SCHEMA "." TABLE   /* the table */
    " WHERE x = 1 "             /* the filter */ 
                ;

出力バッファの初期化を忘れないでください。 strcatの最初の引数は、結果の文字列に十分な追加スペースが割り当てられたnullで終わる文字列でなければなりません:

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars

strcat()の最初の引数は、連結された文字列に十分なスペースを保持できる必要があります。そのため、結果を受け取るのに十分なスペースを持つバッファーを割り当てます。

char bigEnough[64] = "";

strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);

/* and so on */

strcat()は、2番目の引数を最初の引数と連結し、結果を最初の引数に格納します。返されるchar *は、単にこの最初の引数であり、便宜上のみです。

1番目と2番目の引数が連結された、新しく割り当てられた文字列は取得しません。これは、コードに基づいて予想したことでしょう。

人々が指摘したように、文字列の処理は大幅に改善されました。そのため、Cスタイルの文字列の代わりにC ++文字列ライブラリを使用する方法を学びたい場合があります。ただし、これは純粋なCのソリューションです

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

void appendToHello(const char *s) {
    const char *const hello = "hello ";

    const size_t sLength     = strlen(s);
    const size_t helloLength = strlen(hello);
    const size_t totalLength = sLength + helloLength;

    char *const strBuf = malloc(totalLength + 1);
    if (strBuf == NULL) {
        fprintf(stderr, "malloc failed\n");
        exit(EXIT_FAILURE);
    }

    strcpy(strBuf, hello);
    strcpy(strBuf + helloLength, s);

    puts(strBuf);

    free(strBuf);

}

int main (void) {
    appendToHello("blah blah");
    return 0;
}

正しいか安全かはわかりませんが、現時点ではANSI Cでこれを行うより良い方法を見つけることができませんでした。

バッファサイズを制限せずにそれを行う最良の方法は、asprintf()を使用することです

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}

文字列リテラルを変更しようとする動作は未定義です。これは次のようなものです:

strcat ("Hello, ", name);

実行しようとします。明確に定義されていない文字列リテラルnameの最後まで"Hello, "文字列を追加しようとします。

これを試してください。あなたがやろうとしているように見えることを達成します:

char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);

これにより、変更が許可されている バッファ領域が作成され、文字列リテラルと他のテキストの両方がそこにコピーされます。バッファオーバーフローに注意してください。入力データを制御する(または事前に確認する)場合は、私が持っているような固定長のバッファーを使用しても構いません。

それ以外の場合は、ヒープから十分なメモリを割り当てて、確実に処理できるようにするなどの軽減戦略を使用する必要があります。つまり、次のようなものです。

const static char TEXT[] = "TEXT ";

// Make *sure* you have enough space.

char *message = malloc (sizeof(TEXT) + strlen(var) + 1);
if (message == NULL)
     handleOutOfMemoryIntelligently();
strcpy (message, TEXT);
strcat (message, var);

// Need to free message at some point after you're done with it.

strcat()と同じことを行う独自の関数を作成できますが、それは何も変更しません:

#define MAX_STRING_LENGTH 1000
char *strcat_const(const char *str1,const char *str2){
    static char buffer[MAX_STRING_LENGTH];
    strncpy(buffer,str1,MAX_STRING_LENGTH);
    if(strlen(str1) < MAX_STRING_LENGTH){
        strncat(buffer,str2,MAX_STRING_LENGTH - strlen(buffer));
    }
    buffer[MAX_STRING_LENGTH - 1] = '\0';
    return buffer;
}

int main(int argc,char *argv[]){
    printf("%s",strcat_const("Hello ","world"));    //Prints "Hello world"
    return 0;
}

両方の文字列が1000文字を超える場合、文字列は1000文字にカットされます。必要に応じてMAX_STRING_LENGTHの値を変更できます。

char *ではなくchar [fixed_size]があると仮定すると、単一のクリエイティブマクロを使用して、<<cout<<like順序(<!> quot; rather%s the disjointed%s \ n <!> quot;、<!> quot; than <!> quot;、<!> quot; printfスタイルformat <!> quot;)。組み込みシステムを使用している場合、このメソッドを使用すると、mallocと*printfなどの大きなsnprintf()ファミリの関数を省くこともできます(これにより、dietlibcが* printfについても文句を言うのを防ぎます)

#include <unistd.h> //for the write example
//note: you should check if offset==sizeof(buf) after use
#define strcpyALL(buf, offset, ...) do{ \
    char *bp=(char*)(buf+offset); /*so we can add to the end of a string*/ \
    const char *s, \
    *a[] = { __VA_ARGS__,NULL}, \
    **ss=a; \
    while((s=*ss++)) \
         while((*s)&&(++offset<(int)sizeof(buf))) \
            *bp++=*s++; \
    if (offset!=sizeof(buf))*bp=0; \
}while(0)

char buf[256];
int len=0;

strcpyALL(buf,len,
    "The config file is in:\n\t",getenv("HOME"),"/.config/",argv[0],"/config.rc\n"
);
if (len<sizeof(buf))
    write(1,buf,len); //outputs our message to stdout
else
    write(2,"error\n",6);

//but we can keep adding on because we kept track of the length
//this allows printf-like buffering to minimize number of syscalls to write
//set len back to 0 if you don't want this behavior
strcpyALL(buf,len,"Thanks for using ",argv[0],"!\n");
if (len<sizeof(buf))
    write(1,buf,len); //outputs both messages
else
    write(2,"error\n",6);
  • 注1、通常、このようなargv [0]は使用しません-単なる例です
  • 注2、整数を文字列型に変換するためのitoa()などの非標準関数を含む、char *を出力する任意の関数を使用できます。
  • 注3、プログラムのどこかで既にprintfを使用している場合、snprintf()を使用しない理由はありません。コンパイルされたコードが大きくなります(ただし、インライン化され、非常に高速になります)
int main()
{
    char input[100];
    gets(input);

    char str[101];
    strcpy(str, " ");
    strcat(str, input);

    char *p = str;

    while(*p) {
       if(*p == ' ' && isalpha(*(p+1)) != 0)
           printf("%c",*(p+1));
       p++;
    }

    return 0;
}

Cの経験がある場合、文字列は最後の文字がヌル文字であるchar配列のみであることに気付くでしょう。

何かを追加するには最後の文字を見つけなければならないので、これは非常に不便です。 strcatはあなたのためにそれを行います。

したがって、strcatは最初の引数でヌル文字を検索します。次に、これを2番目の引数のコンテンツ(nullで終わるまで)に置き換えます。

次に、コードを見てみましょう:

message = strcat("TEXT " + var);

ここでは、テキスト<!> quot; TEXT <!> quotへのポインタに何かを追加しています。 (<!> quot; TEXT <!> quot;の型はconst char *です。ポインター。)。

通常は機能しません。 <!> quot; TEXT <!> quotも変更します。配列は通常一定のセグメントに配置されるため機能しません。

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

静的テキストを再度修正しようとしている場合を除いて、それはうまくいくかもしれません。 strcatは結果に新しいメモリを割り当てていません。

代わりに次のようなことをすることを提案します。

sprintf(message2, "TEXT %s TEXT %s", foo, bar);

sprintfのドキュメントを読んで、オプションを確認してください。

そして今重要なポイント:

テキストとヌル文字を保持するのに十分なスペースがバッファにあることを確認します。あなたを助けることができる関数がいくつかあります。例えば、strncatと、あなたのためにバッファを割り当てるprintfの特別なバージョンです。 バッファサイズを確保しないと、メモリが破損し、リモートで悪用可能なバグが発生します。

これは私の解決策でした

#include <stdlib.h>
#include <stdarg.h>

char *strconcat(int num_args, ...) {
    int strsize = 0;
    va_list ap;
    va_start(ap, num_args);
    for (int i = 0; i < num_args; i++) 
        strsize += strlen(va_arg(ap, char*));

    char *res = malloc(strsize+1);
    strsize = 0;
    va_start(ap, num_args);
    for (int i = 0; i < num_args; i++) {
        char *s = va_arg(ap, char*);
        strcpy(res+strsize, s);
        strsize += strlen(s);
    }
    va_end(ap);
    res[strsize] = '\0';

    return res;
}

ただし、連結する文字列の数を指定する必要があります

char *str = strconcat(3, "testing ", "this ", "thing");

これに似たものを試してください:

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

int main(int argc, const char * argv[])
{
  // Insert code here...
  char firstname[100], secondname[100];
  printf("Enter First Name: ");
  fgets(firstname, 100, stdin);
  printf("Enter Second Name: ");
  fgets(secondname,100,stdin);
  firstname[strlen(firstname)-1]= '\0';
  printf("fullname is %s %s", firstname, secondname);

  return 0;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top