-3
#include <iostream>
#include <fstream>
#include <cstring>




struct Data {
  char *Hold1;
  char *Hold2;
};



int main() {
  
//Variables
  int choice , loop = 0, RecAmount = 0;
  std::string DepName, output1;
  double inp2, output2;

////File Check
   std::fstream inventory;
    inventory.open("inventory.dat", std::ios::out|std::ios::in|std::ios::binary );  
    if (inventory.fail()){
      inventory.open("inventory.dat", std::ios::out|std::ios::in | std::ios::binary|           
      std::ios::trunc);
      std::cout << "Error opening file....";
        return 0; }

  while (loop < 1) {
    std::cout << "\nRead/write testing\n";
    std::cout << "1. Input Data\n";
    std::cout << "2. Read Data Output\n";
    std::cin >> choice;

////Input 2 strings
    if (choice == 1) {
      std::cout << "Testing input\n";

      struct Data test1;
      test1.Hold1 = "First test";
      test1.Hold2 = 29;

      
     
      }

    if (choice == 2) {
      std::cout << "Testing output\n";
      
      //// Add way to choose which data is read and displayed
      inventory.read ((char *)& inp2, sizeof(Data));

      

      std::cout << "Character input = " << output1 << std::endl;
    }
  }
}

I don't know how to access or use Binary files. I need to:

  • Write numbers and character arrays to a binary file (named "inventory.dat" in this)
  • Be able to seek and read back chosen information (say I input 3 sets of data and want to only display the 2nd set)
  • Overwrite any chosen data (find the second set of data and change it)

I've tried looking up info before and couldn't find anything I could understand or use so any help would be greatly appreciated.

BigBear21
  • 1
  • 2
  • [std::fseek](https://en.cppreference.com/w/cpp/io/c/fseek). However, you will have difficulty dumping a `std::string` to a file. – 001 Dec 12 '22 at 18:41
  • How do I use Fseek in this example? Also I turn my string into a character array but still dont know how to write or read to the file – BigBear21 Dec 12 '22 at 18:43
  • 1
    `DepName` and `output1` are not [POD types](https://stackoverflow.com/questions/146452/what-are-pod-types-in-c) so you can't write/read them from a file by casting to `char*`. You either need to use a serialisation library or write your own serialisation code that properly writes and structures your data – Alan Birtles Dec 12 '22 at 18:44
  • what should I change exactly to get to my desired result? I know strings are not usable in a binary file but I don't understand how to get something that is. If I made Output1 a char array as well would it read correctly? – BigBear21 Dec 12 '22 at 18:50
  • If you used a `char` array it would be easier. Create a `struct` to hold the data. Then "seek" around the file using `sizeof(your_struct)` bytes. – 001 Dec 12 '22 at 18:52
  • I've changed my code to include a structure. How do I put the char array in the struct then read/write it to the file? – BigBear21 Dec 12 '22 at 19:00
  • Always search the internet first. Search for "C++ read write binary file serialization". Writing and reading objects is called *serialization*. – Thomas Matthews Dec 12 '22 at 19:04
  • I have. I couldn't understand it so I asked for my specific example so I could fully understand what I was doing. – BigBear21 Dec 12 '22 at 19:06
  • @BigBear21 You wrote a lot of code, and from the very beginning, this was not going to work. The simple reason why this could never work is this: `inventory.read(sizeof(Data));`. What is `sizeof(Data)`? It is a *compile-time* value -- let's say it is 72. That will not change, regardless of the number of characters there are in the `string`. The `read` function requires the number of bytes to read as the parameter. If one of those strings contained a thousand characters, `sizeof(Data)` will not magically change into something greater than 1000. It will remain at 72. – PaulMcKenzie Dec 12 '22 at 19:09
  • I know it wouldnt work I just dont know what to input as parameters so it can. – BigBear21 Dec 12 '22 at 19:11
  • Also, what is the fascination that so many new programmers have with reading and writing binary files? Your issue using `std::string` (and other types) has appeared maybe thousands of times here. It seems someone or something is giving incentive to write code that is not correct. Maybe [this erroneous code sample](https://www.tutorialspoint.com/reading-and-writing-binary-file-in-c-cplusplus) is what is causing this phenomenom? – PaulMcKenzie Dec 12 '22 at 19:12
  • @BigBear21 -- Obviously you didn't think this through enough before starting. To properly serialize data to a "binary file", you have to first define the layout of the file -- what data is placed where in each record of the file. Then you have to devise a method of saving the *data* to the file in accordance to the layout. Then you have to write code that can read the data back, following the layout, into variables (it doesn't even have to be a `struct`) so that you get the correct data back. That in a nutshell is what is required. – PaulMcKenzie Dec 12 '22 at 19:15
  • How would I do that? – BigBear21 Dec 12 '22 at 19:17
  • Start with what was already mentioned. Use char arrays of a set size instead of `std::string`. – PaulMcKenzie Dec 12 '22 at 19:20
  • I assume for the 2 `Depname` and `DepHeadName` you can create a char array of some fixed size like 64 or 100 characters where you will expect no one to have a name longer than this. – drescherjm Dec 12 '22 at 19:31
  • I changed it to try to get in the right direction but I still dont understand. Can someone give me an example please? – BigBear21 Dec 12 '22 at 19:42

1 Answers1

1

Here's an example using a char array. This is not quite complete but should get you started in the right direction. If definitely needs more error checking.

const int BUFFER_SIZE = 256;

// A FIXED SIZE data structure
struct data {
    char string1[BUFFER_SIZE];
    char string2[BUFFER_SIZE];
    int i1;
    double d1;
};
if (choice == 1) {
    data test;
    std::cin >> test.string1;
    std::cin >> test.string2;
    std::cin >> test.i1;
    std::cin >> test.d1;
    // Append to end of file
    inventory.seekg(0, std::ios_base::end);
    inventory.write(reinterpret_cast<const char *>(&test), sizeof(data));
}
else {
    int recordNumber;
    std::cout << "Enter record number to read: ";
    std::cin >> recordNumber;
    data test;
    // Jump to record.
    // Ex: user enters "5". To get the file pointer to point to record #5
    // seek from the beginning of the file to 4 * sizeof(a single record)
    if (inventory.seekg((recordNumber - 1) * sizeof(test))) {
        inventory.read(reinterpret_cast<char *>(&test), sizeof(test));
        std::cout << test.string1 << ','
                  << test.string2 << ','
                  << test.i1 << ','
                  << test.d1;

    }
}
001
  • 13,291
  • 5
  • 35
  • 66
  • This is great! Unfortunately the "&test& in the write and the "cin << recordNumber" stop the example from working but I can kinda see what Im supposed to do now. What would I change for it to work? – BigBear21 Dec 12 '22 at 20:44
  • For the first, you might have to cast to `const char *`. The second one is a typo. Should be `std::cin >> recordNumber;` – 001 Dec 12 '22 at 20:46
  • if (inventory.seekg((recordNumber - 1) * sizeof(test)){ inventory.read(&test, sizeof(test); std::cout << test.string1 << ',' << test.string2 << ',' << test.i1 << ',' << test.d1;} this part isnt working for some reason – BigBear21 Dec 12 '22 at 20:53
  • Same thing - needs a cast. – 001 Dec 12 '22 at 20:55
  • I input 3 sets of data but when I try to search it doenst work. I put (Test1,Test1,10,10) (Test2,Test2,20,20) and (Test3,Test3,30,30). It writes it to the file but it doesnt read correctly. It overwrites data with each try and when I just start the program by reading its garbage text. I dont think its moving the "get" cursor correctly but I dont know how to fix that – BigBear21 Dec 12 '22 at 21:03
  • Changing the open mode to `std::ios::out | std::ios::in | std::ios::binary | std:: ios::app` works. – 001 Dec 12 '22 at 21:10
  • 1
    THANK YOU SO MUCH! It finally works. You dont know how long Ive been trying to figure this out. I will have to implement it into a program later but I think I understand a bit more now. – BigBear21 Dec 12 '22 at 21:16
  • Last thing. If I wanted to change the amount of data I write would I have to change anything? like the cursor movement? – BigBear21 Dec 12 '22 at 22:00
  • The reason I put everything in a struct is that you just need `sizeof(your_struct)` to determine how much to skip around. If you change the struct, nothing else needs to change. You don't have to use a struct, but you would need to manually calculate the size. – 001 Dec 13 '22 at 14:47