2010-09-06 8 views
1

je dois stocker les chaînes suivantes dans 3 char * variables dans C:Modification des chaînes en C

[foo] 
[foo]:[bar] 
[foo]:[bar]:[tat] 

Chaque chaîne individuelle comme « foo » est reçu à un moment donné du temps via un pointeur char *, la chaîne de sortie correspondante est produite immédiatement avant la réception de la deuxième chaîne, et le nombre total de chaînes individuelles est connu à tout moment.

Il peut y avoir un nombre arbitraire de chaînes.

j'ai écrit le code suivant pour imprimer les chaînes:

for(i=0;i<num;i++) 
    { 
     for(j=0;j<=i;j++) 
      { 
       printf("[%s]",str[j]); 
       if(j!=i) 
       { 
        printf(":"); 
       } 
       else 
       { 
        printf("\n"); 
       } 
      }  
    } 

Mais je suis incapable de trouver un moyen de stocker les chaînes de sortie souhaitées dans les variables, autre que l'écriture d'une fonction au cas par cas en désordre en utilisant strcat() & certaines fonctions supplémentaires auto-définies.

Existe-t-il un moyen de le faire, qui semble aussi propre que d'imprimer les chaînes?

+0

Est-ce que ce travail est fait? – Earlz

+0

non, c'est pour un petit interpréteur de ligne de commande j'essaye d'écrire qui enregistre les commandes tapées. –

+1

Vous avez une faute de frappe. Le 'if()' devrait vérifier 'j! = I', pas' i! = Num-1'. Mais je suppose que vous avez déjà eu la version de travail et ce n'était qu'un échantillon, non? :-) –

Répondre

3

Il s'agit d'un problème de gestion de la mémoire et de gestion de chaîne dans C-are-fastidieux. Si je ne me trompe pas en pensant que pour les chaînes d'entrée n, vous voulez les chaînes de sortie n, chacun plus que le dernier, je ferais ceci:

char **results = malloc(num * sizeof(char*)); 
if (results == 0) return ENOMEM; 
int lastlen = 0; 
for (int i = 0; i < num; ++i) { 
    char *newresult = malloc(lastlen + strlen(str[i]) + 4); 
    if (newresult == 0) { /* even more tedious freeing code goes here */ } 
    results[i] = newresult; 
    char *ptr = newresult; 
    if (i != 0) { 
     ptr += sprintf(ptr, "%s:", results[i-1]) 
    } 
    sprintf(ptr, "[%s]", str[i]) 
    lastlen = strlen(newresult); 
} 

Ou quelque chose comme ça. Évidemment, il y a une bonne opportunité d'écrire plus d'une fonction ici, pour séparer la génération de ces chaînes du stockage de celles-ci dans des tableaux. Il y a aussi l'argument habituel que nous pouvons faire mieux dans les enjeux du risque de débordement de tampon que dans l'arithmétique mentale sprintf.

Modifier: a volé l'utilisation de sprintf de Oskar.

Autre modification: exemple avec asprintf, disponible sur GNU et BSD. Cela a inclus le code de libération fastidieux, le chemin de la réussite est en fait assez bon. Retourne un tableau nouvellement alloué en cas de succès, 0 en cas d'erreur. L'appelant est supposé connaître num et donc savoir combien de temps le tableau sera: si cela ne sert à rien, alors le tableau pourrait être terminé par un caractère nul.

char **results = malloc(num * sizeof(char*)); 
if (results != 0) { 
    const char *last = ""; 
    const char *sep = ""; 
    for (int i = 0; i < num; ++i) { 
     if (asprintf(results + i, "%s%s[%s]", last, sep, str[i]) == -1) break; 
     last = str[i]; 
     sep = ":"; 
    } 
    if (i == num) return results; // success! 
    // asprintf failed, so the value of results[i] is undefined 
    while (i != 0) free(results[--i]); 
    free(results); 
} 
return 0; 
+1

+1, j'ai mal lu la question. Mon dernier point est toujours, passez tout ce que vous faites à travers valgrind ou l'équivalent de votre plate-forme, pour vérifier ces dépassements de tampon satanés. –

+0

@Ninefingers: true. Et sur ce point, il est probablement préférable d'écrire une fonction qui prend deux chaînes en entrée, et renvoie un "% s: [% s]" nouvellement alloué en sortie, plutôt que de tout mélanger dans la boucle comme je l'ai fait. Plus facile de voir que la taille de la mémoire tampon est correcte, puis utilisez vos fonctions de chaîne de longueur cochée préférées pour les implémenter. Je préfère 'strlcpy' et une vérification de troncature qui panique fort, sur' strncpy', mais c'est un argument séparé ... –

+0

Vous pouvez utiliser snprintf(). – Nyan

1

Vous pouvez utiliser sprintf(). En utilisant votre code comme exemple:

for(i=0;i<num;i++) 
{ 
    char s[N]; 
    s[0] = '\0'; 
    for(j=0;j<=i;j++) 
     { 
      sprintf(s+strlen(s),"[%s]",str[j]); 
      if(i!=j) 
      { 
       sprintf(s+strlen(s),":"); 
      } 
      else 
      { 
       sprintf(s+strlen(s),"\n"); 
      } 
     }  
    /* store 's' somewhere */ 
} 

Edit: je ne recommanderais pas cette solution pour quelque chose de sérieux. Mais pour une application simple où vous voulez juste faire avancer les choses, cela devrait faire l'affaire. Juste avoir N assez grand, ou l'allouer dynamiquement pour être en mesure de contenir la plus grande chaîne.

+0

Plutôt que de devoir constamment recalculer 'strlen (s)', vous pouvez garder un pointeur en cours d'exécution à la fin de la chaîne en faisant 'p + = sprintf (p, "[% s]", str [j]); '(' sprintf() 'renvoie le nombre de caractères copiés, sans compter le terminateur null). – caf

1

Vous avez deux questions distinctes: vous assurant allouer suffisamment d'espace et aussi créant la chaîne. Il y a beaucoup de façons d'équilibrer les deux, mais en utilisant realloc(), il est assez simple. Voici une solution qui vérifie et, si nécessaire, agrandit le tampon.

Les compromis sont essentiellement la complexité du code vs performance, tels que:

  • Calcul des besoins en mémoire et en allouant une fois par rapport à le faire plus simplement (à mon humble avis ) dans la boucle.
  • Déperdition de mémoire par surallocation vs perte de temps par (ré) allocation fréquente.
  • En utilisant le strcpy() plus simple par rapport à une utilisation plus simple avec sprintf().

Ma version

char *joinCmds(char* str[], /* The strings to concatenate */ 
       int num) /* the number of strings */ 
{ 
    int len = 0, i, off = 0; 
    char *out = NULL, sep; 

    for(i=0; i<num; i++) 
    { 
     while (len < (off+4+strlen(str[i]))) { 
      len += 16384;  /* Larger numbers wastes memory in for better performance */ 
      out = realloc(out, len); /* Warning: some 'realloc()' take 3 parameters */ 
      /** Handle NULL (Out of Memory errors) */ 
     } 
     sep = (i < (num-1)) ? ':' : '\n'; 
     off += sprintf(&out[off], "[%s]%c", str[i], sep); 
    } 
    return out; 
} 
+0

pourquoi utilisez-vous 16384? Je ne comprends pas ce que vous entendez par "De plus grands nombres gaspillent de la mémoire pour de meilleures performances". –

0

une solution avec strcpy; avec sprintf c'est aussi facile.

char out[1000],*p=out; /* out with enough size */ 
int i=0; 
/* num > 0 */ 
for(; i<num && (p+=strlen(strcpy(p,"]:["))) ; p+=strlen(strcpy(p,str[i++]))) ; 
strcpy(p,"]"); 
puts(out+2);