2017-09-13 5 views
1

Je travaille sur un petit projet où je dois gérer les fichiers d'E/S (c'est quelque chose que je suis nouveau à). J'utilise l'API WIN32 avec unicode comme jeu de caractères et donc stocke toutes les données de fichier en utilisant des caractères larges, et toutes les chaînes du programme sont stockées en utilisant std :: wstring. Ceci est la partie de la fonction qui lit et renvoie les chaînes:Fin de WCHAR * contient junk

  //Get the string from file and return it 
      //(nChars is the amount of characters to read) 
      WCHAR * resultBuffer = new WCHAR[nChars]; 
      file.read(resultBuffer, nChars); 
      std::wstring result = resultBuffer; 
      delete[] resultBuffer; 
      return result; 

Cependant j'ai remarqué ce résultat contient un tas de personnages à ordures à la fin (la chaîne entière est lue correctement à partir du fichier, mais avec des caractères garbage annexés à la fin). Après une inspection plus poussée, j'ai remarqué que ces caractères apparaissent également juste après l'attribution de resultBuffer. Maintenant, ce ne serait pas un problème s'ils étaient écrasés mais semblaient simplement être ajoutés, et ils étaient copiés pour obtenir un résultat (ce qui signifie que le résultat obtient plus d'éléments que prévu), ce qui conduit à beaucoup de problèmes. J'ai réussi à résoudre le problème en y ajoutant quelque chose:

  //Get the string from file and return it 
      WCHAR * resultBuffer = new WCHAR[nChars]; 
      file.read(resultBuffer, nChars); 
      std::wstring temp = resultBuffer; 
      std::wstring result; 
      for (INT i = 0; i < nChars; i++) { //NOTE: This shouldn't be necessary 
       result.push_back(temp.at(i)); 
      }    
      delete[] resultBuffer; 
      return result; 

Cela résout le problème mais je me sens comme si elle ne devrait pas être nécessaire. Je pensais que cela pouvait avoir quelque chose à voir avec le fonctionnement de la fonction de lecture (std :: wifstream :: read()), mais j'ai regardé la documentation et je n'ai trouvé aucun indice. Je n'ai pas beaucoup d'expérience avec l'utilisation d'unicode et de caractères larges, donc c'est peut-être quelque chose qui me manque, mais je suis vraiment hors de question. Quelqu'un a une idée? C'est ainsi que resultBuffer s'occupe de read() (stackoverflow les affiche comme des caractères du Moyen-Orient, mais ils apparaissent comme des caractères asiatiques dans Visual Studio).

  • resultBuffer L "\\. \ DISPLAY1 ﷽﷽☐☐كي 헏 ✀ 耀 ☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐☐ ☐ » wchar_t *

EDIT: Merci à Remy Lebeau et mksteve pour fournir de grandes explications aswell comme réponses! Voici le code de travail:

  //Get the string from file and return it 
      std::wstring result; 
      result.resize(nChars); 
      file.read(&result[0], nChars); 
      return result; 

Répondre

3

Vous appelez le constructeur std::wstring qui attend une chaîne wchar_t* zéro terminal, mais vous n'êtes pas nul résiliation de votre tampon.Allouer +1 plus wchar et le mettre à 0:

WCHAR * resultBuffer = new WCHAR[nChars+1]; 
file.read(resultBuffer, nChars); 
resultBuffer[nChars] = L'\0'; 
std::wstring result = resultBuffer; 
delete[] resultBuffer; 
return result; 

Sinon, si vous spécifiez la longueur de la mémoire tampon lors de la construction du std::wstring, vous n'avez pas besoin de terminaison null:

WCHAR * resultBuffer = new WCHAR[nChars]; 
file.read(resultBuffer, nChars); 
std::wstring result(resultBuffer, nChars); 
delete[] resultBuffer; 
return result; 

De toute façon, vous devez utiliser std::vector pour gérer la mémoire tampon au lieu d'utiliser new[]/delete[] manuellement:

std::vector<WCHAR> resultBuffer(nChars+1); 
file.read(&resultBuffer[0], nChars); 
resultBuffer[nChars] = L'\0'; 
return std::wstring(resultBuffer.data()); 

std::vector<WCHAR> resultBuffer(nChars); 
file.read(&resultBuffer[0], nChars); 
return std::wstring(resultBuffer.data(), nChars); 

Ou, vous pouvez vous débarrasser du tampon tout à fait et juste lire directement dans le std::wstring lui-même:

std::wstring result; 
result.resize(nChars); 
file.read(&result[0], nChars); // or result.data() in C++17 
return result; 
+0

Avec C++ 17, vous pouvez également écrire immédiatement dans un 'std :: wstring', en transmettant le résultat de ['std :: string :: data()'] (http://en.cppreference.com/w/cpp/string/basic_string/data) dans 'file.read()'. – IInspectable

+1

Avant C++ 17, vous pouvez utiliser la chaîne 'operator []' à la place. –

+0

Si vous utilisez 'std :: vector', il n'est pas nécessaire de définir le terminateur vous-même, il sera déjà initialisé par défaut à zéro. –

1

Lorsque vous avez lu n caractères à partir d'un tampon, le mécanisme pour créer un std::string est d'utiliser le constructeur de taille

file.read(resultBuffer, nChars); 
std::wstring temp(resultBuffer, nChars); 

Ce chiffre est légèrement différence de terminaison null votre entrée, car elle permet d'inclure resultBuffer l « \ 0 », qui devient par t de la nouvelle chaîne. Si cela n'est pas correct, assurez-vous que les données sont terminées à zéro après le nombre d'octets lus à partir du file.read