2016-06-22 1 views
0

Tout d'abord, je sais qu'il ya des messages avec des problèmes similaires, mais je ne trouve pas la solution à la mienne dans l'un d'entre eux.Accéder à la violation de lecture de l'emplacement en utilisant le fichier binaire

Il s'agit d'une affectation de programmation utilisant des fichiers binaires et des fichiers texte pour stocker les données de vente d'entreprise. (Nom de division, trimestre et ventes), puis pour rechercher des enregistrements spécifiés dans le fichier de données binaires et les afficher.

Voici les parties importantes de mon code:

#include stuff 
... 

// Struct to hold division data 
struct DIVISION_DATA_S 
{ 
    string divisionName; 
    int quarter; 
    double sales; 
}; 

int main() 
{ 
    ... 

    // Open the data file 
    fstream dataFile; 
    dataFile.open(dataFilePath, ios::in | ios::out | ios::binary); 

    ... Get data from user, store in an instance of my struct ... 

    // Dump struct into binary file 
    dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));   

    // Cycle through the targets file and display the record from divisiondata.dat for each entry 
while(targetsFile >> targetDivisionName) 
{  
    int targetQuarter; // Target quarter 
    string targetQuarterStr; 
    targetsFile.ignore(); // Ignore the residual '\n' from the ">>" read 
    getline(targetsFile, targetQuarterStr); 
    targetQuarter = atoi(targetQuarterStr.c_str()); // Parses into an int 

    cout << "Target: " << targetDivisionName << " " << targetQuarter << endl; 

    // Linear search the data file for the required name and quarter to find sales amount 
    double salesOfTarget; 
    bool isFound = false; 
    while (!isFound && !dataFile.eof()) 
    { 
     cout << "Found division data: " << targetDivisionName << " " << targetQuarter << endl; 
     DIVISION_DATA_S divisionData; 

     // Read an object from the file, cast as DIVISION_DATA_S 
     dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData)); 
     cout << "Successfully read data for " << targetDivisionName << " " << targetQuarter << endl 
      << "Name: " << divisionData.divisionName << ", Q: " << divisionData.quarter << ", " 
      << "Sales: " << divisionData.sales << endl; 

     // Test for a match of both fields 
     if (divisionData.divisionName == targetDivisionName && divisionData.quarter == targetQuarter) 
     { 
      isFound = true; 
      cout << "Match!" << endl; 
      salesOfTarget = divisionData.sales; 
     } 
    } 
    if (!isFound) // Error message if record is not found in data file 
    { 
     cout << "\nError. Could not find record for " << targetDivisionName 
      << " division, quarter " << targetQuarter << endl; 
    } 
    else 
    { 
     // Display the corresponding record 
     cout << "Division: " << targetDivisionName << ", Quarter: " << targetQuarter 
      << "Sales: " << salesOfTarget << endl; 
     totalSales += salesOfTarget; // Add current sales to the sales accumulator 
     numberOfSalesFound++; // Increment total number of sales found 
    } 
} 

Désolé pour l'absence de tiret pour la boucle while, copier/coller type de foiré.

Mon problème apparaît lorsque j'essaie d'accéder aux informations lues dans le fichier binaire. Par exemple, quand il essaie d'exécuter l'instruction cout j'ai ajouté pour le débogage, il me donne cette erreur:

Unhandled exception at 0x0FED70B6 (msvcp140d.dll) in CorporateSalesData.exe: 0xC0000005: Access violation reading location 0x310A0D68. 

Maintenant, ce que j'ai lu, il semble que cela signifie quelque chose essaie de lire tout Les régions basses de mémoire, AKA quelque chose a quelque chose à faire avec un pointeur nul, mais je ne peux pas imaginer comment cela apparaîtrait. Toute cette opération de lecture est copiée exactement à partir de mon manuel, et je n'ai aucune idée de ce qu'est un reinterpret_chast, encore moins comment cela fonctionne ou comment corriger les erreurs avec. S'il vous plaît aider?

EDIT: Merci pour toute l'aide. Pour éviter les complications ou utiliser quelque chose que je ne comprends pas complètement, je vais passer à un c-string pour le divisionName.

+2

Avez-vous essayé parcourant le code dans un débogueur? – rrirower

+5

La lecture et l'écriture des octets d'un 'std :: string' (ou de la plupart des objets de classe légèrement compliqués) ne fonctionnent pas. Vous devez écrire la valeur de chaque élément séparément (ou utiliser un paquet de sérialisation). –

+1

Conseil général: Évitez d'utiliser 'reinterpret_cast' sauf si vous devez absolument absolument l'utiliser dans un but particulier. –

Répondre

1
// Dump struct into binary file 
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData)); 

/*...*/ 

// Read an object from the file, cast as DIVISION_DATA_S 
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData)); 

Cela ne fonctionnera catégoriquement en aucun cas.

std::string utilise des pointeurs alloués par tas pour stocker les données de chaîne qu'il contient. Ce que vous écrivez dans le fichier est pas le contenu de la chaîne, mais simplement l'adresse où se trouvent les données de la chaîne (avec quelques méta-données). Si vous lisez ces pointeurs arbitrairement et les traitez comme de la mémoire (comme dans l'instruction cout), vous référerez la mémoire supprimée.

Vous avez deux options.

Si vous voulez tout est une struct qui peut facilement être sérialisé, puis convertir simplement comme ceci:

// Struct to hold division data 
struct DIVISION_DATA_S 
{ 
    char divisionName[500]; 
    int quarter; 
    double sales; 
}; 

Bien sûr, avec ce style, vous êtes limité à interagir avec le nom comme c -string, et sont également limités à 500 caractères.

L'autre option consiste à correctement sérialiser cet objet.

// Struct to hold division data 
struct DIVISION_DATA_S 
{ 
    string divisionName; 
    int quarter; 
    double sales; 

    string serialize() const { //Could also have the signature be std::vector<char>, but this will make writing with it easier. 
     string output; 
     std::array<char, 8> size_array; 
     size_t size_of_string = divisionName.size(); 
     for(char & c : size_array) { 
      c = size_of_string & 0xFF; 
      size_of_string >>= 8; 
     } 
     output.insert(output.end(), size_array.begin(), size_array.end()); 
     output.insert(output.end(), divisionName.begin(), divisionName.end()); 
     int temp_quarter = quarter; 
     for(char & c : size_array) { 
      c = temp_quarter & 0xFF; 
      temp_quarter >>= 8; 
     } 
     output.insert(output.end(), size_array.begin(), size_array.begin() + sizeof(int)); 
     size_t temp_sales = reinterpret_cast<size_t>(sales); 
     for(char & c : size_array) { 
      c = temp_sales & 0xFF; 
      temp_sales >>= 8; 
     } 
     output.insert(output.end(), size_array.begin(), size_array.end()); 
     return output; 
    } 

    size_t unserialize(const string & input) { 
     size_t size_of_string = 0; 
     for(int i = 7; i >= 0; i--) { 
      size_of_string <<= 8; 
      size_of_string += unsigned char(input[i]); 
     } 
     divisionName = input.substr(7, 7 + size_of_string); 
     quarter = 0; 
     for(int i = 10 + size_of_string; i >= 7 + size_of_string; i--) { 
      quarter <<= 8; 
      quarter += unsigned char(input[i]); 
     } 
     size_t temp_sales = 0; 
     for(int i = 18 + size_of_string; i >= 11 + size_of_string; i--) { 
      temp_sales <<= 8; 
      temp_sales += unsigned char(input[i]); 
     } 
     sales = reinterpret_cast<double>(temp_sales); 
     return 8 + size_of_string + 4 + 8; 
    } 
}; 

est assez facile d'écriture à des fichiers:

dataFile << divisionData.serialize(); 

La lecture peut être un peu plus difficile:

stringstream ss; 
ss << dataFile.rdbuf(); 
string file_data = ss.str(); 
size_t size = divisionData.unserialize(file_data); 
file_data = file_data.substr(size); 
size = divisionData.unserialize(file_data); 
/*...*/ 

D'ailleurs, je n'ai pas vérifié mon code pour la syntaxe ou l'exhaustivité . Cet exemple est destiné à servir de référence pour le type de code que vous devez écrire pour sérialiser/désérialiser correctement les objets complexes. Je crois pour être correct, mais je ne voudrais pas jeter dans non testé.

+0

D'accord, gros problème. J'ai troqué la chaîne avec un char [300], mais quand j'essaie de faire 'divisionData.divisionName = name.c_str();' (où 'name' a été juste entré), j'obtiens l'erreur:' L'expression doit être un lvalue modifiable. quelle??? – vasilescur

+0

@ user3284178 Vous rencontrez un problème similaire: vous essayez d'assigner un pointeur. Vous devriez utiliser quelque chose comme ceci: 'std :: copy (name.begin(), name.end(), divisionData.divisionName)'. Notez que dans ce cas, vous devrez vous assurer que vous faites des vérifications de limites appropriées. – Xirema

2
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData)); 

Fonctionne uniquement si vous avez des types de POD. Cela ne fonctionne pas quand vous avez un std::string là-dedans. Vous devrez utiliser quelque chose comme:

// Write the size of the string. 
std::string::size_type size = divisionDat.divisionName.size(); 
dataFile.write(reinterpret_cast<char*>(&size), sizeof(size)); 

// Now write the string. 
dataFile.write(reinterpret_cast<char*>(divisionDat.divisionName.c_str()), size); 

// Write the quarter and the sales. 
dataFile.write(reinterpret_cast<char*>(&divisionDat.quarter), sizeof(divisionDat.quarter)); 
dataFile.write(reinterpret_cast<char*>(&divisionDat.sales), sizeof(divisionDat.sales)); 

Modifiez les appels lus pour qu'ils correspondent aux appels d'écriture.

1

Bienvenue dans le monde de la sérialisation. Vous essayez de «bit blit» votre structure dans un fichier. Cela ne fonctionne que pour les types très simples (int, float, char [xxx]) où les données sont réellement en ligne. Et même quand cela fonctionne, vous êtes obligé de recharger les données dans le même type de machine (même taille de mot, même endianness).

Ce que vous devez faire est de sérilazer les données et de les désérialiser ensuite. Vous pouvez inventer des façons de le faire vous-même ou vous pouvez en utiliser un sur de nombreuses normes. Il existe 2 types de base - binaires (efficaces, pas lisibles par l'homme) et texte (moins efficaces mais lisibles par l'homme)

texte

  • JSON
  • yaml
  • xml
  • csv

binaire

  • protobuf

boost dispose d'une bibliothèque de sérialisation http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/

vous aimerez aussi regarder ici

https://isocpp.org/wiki/faq/serialization