web-dev-qa-db-fra.com

Comment concaténer des chaînes const / literal en C?

Je travaille en C et je dois concaténer quelques petites choses.

En ce moment j'ai ceci:

message = strcat("TEXT ", var);

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

Maintenant, si vous avez de l'expérience en C, je suis sûr que vous réalisez que cela vous donne une erreur de segmentation lorsque vous essayez de l'exécuter. Alors, comment est-ce que je travaille autour de ça?

299
The.Anti.9

En C, les "chaînes" sont simplement de simples tableaux char. Par conséquent, vous ne pouvez pas les concaténer directement avec d'autres "chaînes".

Vous pouvez utiliser la fonction strcat, qui ajoute la chaîne pointée par src à la fin de la chaîne pointée par dest:

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

Voici un exemple de cplusplus.com :

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

Pour le premier paramètre, vous devez fournir le tampon de destination lui-même. Le tampon de destination doit être un tampon de tableau de caractères. Exemple: char buffer[1024];

Assurez-vous que le premier paramètre dispose de suffisamment d'espace pour stocker ce que vous essayez de copier. Si vous en avez la possibilité, il est préférable d’utiliser des fonctions telles que: strcpy_s et strcat_s où vous devez spécifier explicitement la taille du tampon de destination.

Remarque : un littéral de chaîne ne peut pas être utilisé comme tampon car il s'agit d'une constante. Ainsi, vous devez toujours allouer un tableau de caractères pour le tampon.

La valeur de retour de strcat peut simplement être ignorée, elle renvoie simplement le même pointeur que celui qui a été transmis comme premier argument. Il est là pour votre commodité et vous permet de chaîner les appels en une seule ligne de code:

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

Donc, votre problème pourrait être résolu comme suit:

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

Évitez d’utiliser strcat dans le code C. Le moyen le plus propre et le plus sûr est d'utiliser snprintf :

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

Certains commentateurs ont soulevé un problème selon lequel le nombre d'arguments ne correspond peut-être pas à la chaîne de format et que le code sera toujours compilé, mais la plupart des compilateurs envoient déjà un avertissement si tel est le cas.

228
Alex B

Les gens, utilisez strncpy (), strnchat () ou snprintf ().
Si vous dépassez votre espace tampon, tout ce qui suivra en mémoire sera mis au rebut!
(Et n'oubliez pas de laisser de la place pour le dernier caractère '0'!)

23
Mr.Ree

De plus, malloc et realloc sont utiles si vous ne savez pas à l'avance combien de chaînes sont concaténées.

#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

Les chaînes peuvent également être concaténées au moment de la compilation.

#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

N'oubliez pas d'initialiser le tampon de sortie. Le premier argument de strcat doit être une chaîne terminée par un caractère NULL avec suffisamment d'espace supplémentaire alloué pour la chaîne résultante:

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

La meilleure façon de le faire sans avoir une taille de tampon limitée est d'utiliser asprintf ()

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

Comme les gens l'ont souligné, la manipulation des cordes s'est beaucoup améliorée. Vous voudrez peut-être apprendre à utiliser la bibliothèque de chaînes C++ à la place des chaînes de style C. Cependant voici une solution en C pur

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

Je ne suis pas sûr que ce soit correct/sûr mais pour le moment je ne pourrais pas trouver un meilleur moyen de le faire en ANSI C.

4
Nils

Le premier argument de strcat () doit pouvoir contenir suffisamment d'espace pour la chaîne concaténée. Allouez donc un tampon avec suffisamment d’espace pour recevoir le résultat.

char bigEnough[64] = "";

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

/* and so on */

strcat () concaténera le deuxième argument avec le premier argument et stockera le résultat dans le premier argument. Le caractère renvoyé * est tout simplement ce premier argument et uniquement pour votre commodité.

Vous n'obtenez pas une chaîne nouvellement allouée avec le premier et le deuxième arguments concaténés, ce que je suppose que vous attendiez en fonction de votre code.

4
Pieter

Tenter de modifier les littéraux de chaîne est un comportement indéfini, qui ressemble à quelque chose comme:

strcat ("Hello, ", name);

va tenter de faire. Il tentera de clouer la chaîne name à la fin de la chaîne littérale "Hello, ", qui n'est pas bien définie.

Essayez quelque chose ça. Il réalise ce que vous semblez essayer de faire:

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

Cela crée une zone tampon qui est est autorisée à être modifiée, puis copie à la fois le littéral de chaîne et tout autre texte. Soyez juste prudent avec les débordements de mémoire tampon. Si vous contrôlez les données d'entrée (ou si vous les vérifiez à l'avance), vous pouvez utiliser des tampons de longueur fixe, comme je l'ai déjà fait.

Sinon, vous devez utiliser des stratégies d'atténuation telles que l'allocation de suffisamment de mémoire du tas pour vous assurer de pouvoir la gérer. En d'autres termes, quelque chose comme:

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

Vous pouvez écrire votre propre fonction qui fait la même chose que strcat() mais cela ne change rien:

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

Si les deux chaînes ensemble comptent plus de 1000 caractères, la chaîne sera coupée à 1000 caractères. Vous pouvez modifier la valeur de MAX_STRING_LENGTH en fonction de vos besoins.

2
Donald Duck

Si vous avez de l'expérience en C, vous remarquerez que les chaînes ne sont que des tableaux de caractères où le dernier caractère est un caractère nul.

Maintenant, c'est assez gênant car vous devez trouver le dernier caractère pour pouvoir ajouter quelque chose. strcat le fera pour vous.

Donc, strcat cherche dans le premier argument un caractère nul. Ensuite, il remplacera ceci par le contenu du second argument (jusqu'à ce que cela se termine par un null).

Passons maintenant à votre code:

message = strcat("TEXT " + var);

Ici, vous ajoutez quelque chose au pointeur sur le texte "TEXT" (le type de "TEXT" est const char *. Un pointeur.).

Cela ne fonctionnera généralement pas. La modification du tableau "TEXT" ne fonctionnera pas non plus, car il est généralement placé dans un segment constant.

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

Cela pourrait mieux fonctionner, sauf que vous essayez encore de modifier des textes statiques. strcat n'alloue pas de nouvelle mémoire pour le résultat.

Je proposerais de faire quelque chose comme ceci à la place:

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

Lisez la documentation de sprintf pour vérifier ses options.

Et maintenant un point important:

Assurez-vous que le tampon dispose de suffisamment d'espace pour contenir le texte ET le caractère nul. Quelques fonctions peuvent vous aider, par exemple strncat et les versions spéciales de printf qui allouent le tampon à votre place. Ne pas s'assurer que la taille de la mémoire tampon entraînera une corruption de la mémoire et des bogues exploitables à distance.

1
Ralf

En supposant que vous ayez un caractère [fixed_size] plutôt qu'un caractère *, vous pouvez utiliser une seule macro créative pour tout faire en même temps avec un ordre <<cout<<like ("plutôt% s le% s\n disjoint% s\n", "que", " format de style printf "). Si vous travaillez avec des systèmes embarqués, cette méthode vous permettra également d'omettre Malloc et la grande famille de *printfs tels que snprintf() (Ceci empêche également à dietlibc de se plaindre de * 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);
  • Remarque 1, vous n'utiliseriez généralement pas argv [0] comme ceci - juste un exemple
  • Remarque 2, vous pouvez utiliser n'importe quelle fonction générant un caractère *, y compris des fonctions non standard telles que itoa () pour convertir des entiers en types chaîne.
  • Remarque 3, si vous utilisez déjà printf n'importe où dans votre programme, il n'y a aucune raison de ne pas utiliser snprintf (), car le code compilé serait plus volumineux (mais intégré et nettement plus rapide).
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

Vous essayez de copier une chaîne dans une adresse allouée de manière statique. Vous devez chatter dans un tampon.

Plus précisément:

...couper...

destination

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

...couper...

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

Il y a un exemple ici aussi.

1
Todd

C'était ma solution

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

mais vous devez spécifier le nombre de chaînes que vous allez concaténer

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

Essayez quelque chose de semblable à ceci:

#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