2012-06-05 1 views
2

J'ai une application Visual Studio 2008 C++ 03 où je veux copier d'un std :: string à un tableau char, mais j'ai besoin le tableau char est à zéro, même s'il doit tronquer la chaîne pour le faire.copie à partir d'un std :: string à un tableau char et null-terminant le résultat

Ceci, par exemple fonctionne comme vous le souhaitez:

inline void CopyAndNullTerminate(const std::string& source, 
            char* dest, 
            size_t dest_size) 
{ 
    source.copy(dest, dest_size); 
    using std::min; 
    char* end = dest + min(dest_size - 1, source.size()); 
    *end = '\0'; 
} 

int main() 
{ 
    enum{ MAX_STRING_SIZE = 15 }; 
    char dest[ MAX_STRING_SIZE ]; 
    std::string test = "This is a test"; 

    CopyAndNullTerminate(test, dest, MAX_STRING_SIZE); 

    assert(strcmp(test.c_str(), dest) == 0); 
    return 0; 
} 

exemple: http://ideone.com/0EBYb

Y at-il une plus courte, méthode plus efficace de le faire?

Merci

Répondre

6

Oui, utiliser strncpy, définis dans cstring:

void copyString(const std::string& input, char *dst, size_t dst_size) 
{ 
    strncpy(dst, input.c_str(), dst_size - 1); 
    dst[dst_size - 1] = '\0'; 
} 

Notez que pour certaines implémentations de std::string (comme l'a fait par @ K-ballo), cela peut être plus courte, mais moins efficace. Cela est dû au fait que std::string n'est pas garanti pour être implémenté en utilisant des chaînes C-syle, bien que ce soit probablement le cas dans la plupart des situations.

+0

Cela a malheureusement l'effet secondaire de devoir construire une chaîne C validée nulle pour les implémentations de 'std :: string' qui ne l'utilisent pas comme représentation interne. –

+0

@ K-ballo vrai, mais pour la plupart des implémentations, je suppose que ce serait l'implémentation utilisée ... –

+0

Bien que cette méthode soit plus courte, elle peut être moins efficace. Vous devriez le signaler dans votre réponse. –

1

Que diriez-vous du bon ole 'C strcpy_s? Je préfère cela à strncpy. La boucle de copie strcpy_s (et c'est cousin plus ancien strlcpy) se terminera après avoir copié le '\ 0', mais strncpy écrira toujours le nombre d'octets spécifié (remplissage avec '\ 0' si la chaîne source est plus courte).

+0

Malheureusement, 'strlcpy' ne fait pas partie de la norme C, c'est une fonction BSD, pour autant que je sache, et ne sera pas disponible dans VS 2008. –

+0

@ RichardJ.RossIII, merci! Fixé. – jxh

+0

@ RichardJ.RossIII, BTW, la raison pour laquelle je préfère ces fonctions est que 'strncpy' écrit toujours le nombre d'octets spécifiés dans le troisième paramètre, et ne se termine pas par null si la chaîne source est plus longue que les octets spécifiés. – jxh

4

Si vous initialisez votre chaîne de destination (à null) en premier, alors vous pouvez l'obtenir sera terminée par un caractère nul si vous n'écrasez pas le dernier élément de votre tableau.

enum{ MAX_STRING_SIZE = 15 }; 

char dest[ MAX_STRING_SIZE ] = {0}; // all elements are initialized to 0 
std::string test = "This is a test"; 

// determine the length to copy, it will be the min of MAX_STRING_SIZE-1 
// or test.size. This will ensure that even if the string it shorter than 
// the MAX_STRING_SIZE, that it will copy properly -- without reading past the 
// array bounds of the string. 
auto copy_len = std::min(test.size(), MAX_STRING_SIZE - 1); 

std::copy(
    test.begin(), 
    test.begin() + copy_len, 
    dest); 

Cette copie au plus 14 caractères à votre objet dest. Si la chaîne de test est inférieure à 14 caractères, elle copiera uniquement la longueur de la chaîne de test. Tous les éléments non copiés resteront à leur valeur initialisée (null).

6

En supposant que dest_size est assuré d'être au moins 1 (qui me semble raisonnable, car sinon il est impossible de copier quoi que ce soit nulle fin dans le tampon):

inline void CopyAndNullTerminate(const std::string& source, 
            char* dest, 
            size_t dest_size) 
{ 
    dest[source.copy(dest, dest_size-1)] = 0; 
} 

En C++ 11 et dans toutes les implémentations C++ 03 activement gérées (y compris Visual Studio), std::string a le stockage contigu, tout comme std::vector. Ainsi, vous pouvez utiliser memcpy au lieu de std::string::copy et comparer les performances.

De même, vous pouvez comparer strncpy, std::copy, std::copy_n, strcpy_s, et peut-être d'autres, je l'ai oublié, voir ce qui est mieux optimisé. Dans chaque cas, vous pouvez calculer le nombre d'octets à copier comme std::min(source.size(), dest_size-1). Cela évite également le cas le plus inefficace de strncpy (copier une petite chaîne dans un grand tampon).

Quand vous faites tout cela, vous pouvez compter sur le fait qu'il est valide pour appeler source[0] même si source est une chaîne vide.

2

Voici un couple qui sont plus courtes:

inline void CopyAndNullTerminate(const std::string& source, 
            char* dest, 
            size_t dest_size) 
{ 
    *dest = '\0'; // assumes `dest_size > 0` 
    strncat(dest, source, dest_size); 
} 

void CopyAndNullTerminate(std::string const &source, 
          char *dest, 
          size_t dest_size) { 
    sprintf(dest, "%*s", dest_size, source.c_str()); 
} 

efficace peut être ouvert à plus question, mais je ne vois aucune raison de croire soit sera particulièrement lente.

Questions connexes