web-dev-qa-db-ja.com

Cでconst/literal文字列を連結する方法

私はCで働いています、そして私はいくつかのことを連結しなければなりません。

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

message = strcat("TEXT ", var);

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

Cでの経験があるのであれば、実行しようとするとこれがセグメンテーション違反になることを理解していると思います。それでは、どのように私はそれを回避しますか?

297
The.Anti.9

Cでは、 "文字列"は単なる普通のchar配列です。したがって、それらを他の「文字列」と直接連結することはできません。

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_sstrcat_sのような関数を使用する方が安全です。

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

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

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

だからあなたの問題は次のように解決することができます:

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
328
Brian R. Bondy

Cコードでは strcat を使用しないでください。最もきれいで、そして最も重要なことに、 snprintf を使用するのが最も安全な方法です。

char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);

一部のコメント投稿者は、引数の数がフォーマット文字列と一致しない可能性があり、コードはまだコンパイルされるという問題を提起しましたが、そうである場合、ほとんどのコンパイラはすでに警告を発行します。

227
Alex B

皆さん、strを使ってください。ncpy()、strncat()、またはsnprintf().
バッファスペースを超えると、メモリ内の他の部分が駄目になります。
(そして末尾のヌル文字 '\ 0'にはスペースを空けてください!)

23
Mr.Ree

また、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);
}
16
Reed Hedges

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

#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 */ 
                ;
14
dbagnara

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

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

制限されたバッファサイズを使わずにそれをする最良の方法はasprintf()を使うことです。

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

人々が指摘したように、文字列処理はかなり改善されました。ですから、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でこれを行うためのより良い方法を見つけることができませんでした。

4
Nils

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

char bigEnough[64] = "";

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

/* and so on */

strcat()は、2番目の引数と最初の引数を連結し、その結果を最初の引数に格納します。返されたchar *は、単にこの最初の引数になります。

最初の引数と2番目の引数が連結された、新しく割り当てられた文字列を取得することはありません。これは、コードに基づいて想定したとおりです。

4
Pieter

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

strcat ("Hello, ", name);

しようとします。文字列リテラル"Hello, "の最後までname文字列を固定しようとしますが、これは明確に定義されていません。

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

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

これは is に変更を許されるバッファ領域を作成し、それから文字列リテラルと他のテキストの両方をそれにコピーします。バッファオーバーフローに注意してください。あなたが入力データを制御する(あるいは、それを前もってチェックする)なら、私が持っているように固定長バッファを使うのは良いことです。

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

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.
3
paxdiablo

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の値を変更できます。

2
Donald Duck

Cの経験があれば、文字列は最後の文字がnull文字であるchar配列にすぎないことに気付くでしょう。

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

そのため、strcatは最初の引数でNULL文字を検索します。その後、これを2番目の引数の内容に置き換えます(それがnullで終わるまで)。

それでは、コードを見ていきましょう。

message = strcat("TEXT " + var);

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

通常はうまくいきません。また、 "TEXT"配列を変更しても、通常は定数セグメントに配置されるため機能しません。

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

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

私は代わりにこのようなことをすることを提案します:

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

そのオプションをチェックするためにsprintfのドキュメントを読んでください。

そして今重要な点:

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

1
Ralf

Char *ではなくchar [fixed_size]があると仮定すると、1つのクリエイティブマクロを使用して、<<cout<<likeの順序ですべて同時に実行できます( "むしろ%sは分離した%s\n"、 "than"、 " printfスタイルフォーマット ")。組み込みシステムを使っているのであれば、この方法でmallocやsnprintf()のような*printfという大規模な関数ファミリーを省くこともできます(これにより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 *を出力する関数はすべて使用できます。
  • Note 3、あなたがすでにあなたのプログラムのどこかでprintfを使っているのであれば、snprintf()を使わない理由はありません。
1
technosaurus
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;
}
1
Miljan Rakita

静的に割り当てられているアドレスに文字列をコピーしようとしています。あなたはバッファに猫を飼う必要があります。

具体的には:

...スニップ...

Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.

...スニップ...

http://www.cplusplus.com/reference/clibrary/cstring/strcat.html

ここにも例があります。

1
Todd

これは私の解決策でした

#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");
0
Naheel

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

#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;
}
0
jksante