0

I'm trying to convert an mp3 into a binary fstream (or hexadecimal, I can use both). I've figured out how to open the file and output the contents to the console. But in both binary and hex, the leading 0's are removed. Therefore, if I create a new mp3 from the fstream it will be missing many bits. Any help in preserving the original formatting would be appreciated.

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {

    string line;
    ifstream myfile; // Declare variable to hold the location of file

    myfile.open("src/ShortTest.mp3", std::ios::binary); //open short file with sample data

    if (!myfile.is_open())
    {
        cout << "Cannot read file!" << endl;
    }
    else
    {
        cout << "File is open!" << endl;
        while ( getline (myfile,line) ) {
          int n = line.length();
          int remainder = 0;
          for (int i = 0; i <= n; i++) {

            // convert each char to
            // ASCII value
            int val = int(line[i]);

            // Convert ASCII value to binary
            string bin = "";
            while (val > 0) {
              (val % 2)? bin = '1'+bin : bin = '0'+bin ;
              val /= 2;
            }

            cout << bin << " ";
            remainder = (i+1) % 8;
            if ( remainder == 0) {
              cout << endl;
            }
          }

        }
    myfile.close();

    }

    cout << "Done!" << endl;
    return 0;
}

Output:

File is open!
1001001 1000100 110011 11    1 
1001 1100010 1010100 1001001 1010100 110010   
 1100    1010011 1110100 1110101 
1110000 1101001 1100100 100000 1010011 1101111 1101110 1100111 
1010100 1000011 1001111 1001110    111 
   1001111 1110100 1101000 1100101 1110010 
 1010100 1001011 1000101 1011001    
10    1000101 1010100 1011000 1011000 
 Done!

My expected output is what I see in a hex editor

File is open!
01001001 01000100 00110011 00000011 00000000 00000000 00000000 00000001 
00001001 01100010 01010100 01001001 01010100 00110010 00000000 00000000  
00000000 00001100 00000000 00000000 00000000 01010011 01110100 01110101 
01110000 01101001 01100100 00100000 01010011 01101111 01101110 01100111 
01010100 01000011 01001111 01001110 00000000 00000000 00000000 00000111  
00000000 00000000 00000000 01001111 01110100 01101000 01100101 01110010  
00000000 01010100 01001011 01000101 01011001 00000000 00000000 00000000
00000010 00000000 00000000 00000000 01000101 01010100 01011000 01011000  
Done!
Botje
  • 26,269
  • 3
  • 31
  • 41
krose
  • 390
  • 1
  • 6
  • 18
  • 1
    `while (val > 0)` won't account for any leading zeroes. – Eljay Oct 07 '20 at 15:34
  • 2
    When doing the output, you could use [a specific field width](https://en.cppreference.com/w/cpp/io/manip/setw) and [fill the "spaces" with a specific character](https://en.cppreference.com/w/cpp/io/manip/setfill) (e.g. `'0'`). – Some programmer dude Oct 07 '20 at 15:36
  • See [a quick example here](https://onlinegdb.com/ryJ4bvoLP). – Ken Y-N Oct 07 '20 at 15:39

3 Answers3

2

Probably the easiest way to do this is to use std::bitset to do the conversion.

Oh, and using namespace std; and std::endl are both better avoided. Given that you're reading what you assume to be a binary file, std::getline doesn't really make much sense either. Oh, and error messages should normally be written to the standard error stream, not the standard output stream.

#include <iostream>
#include <fstream>
#include <string>
#include <bitset>

int main(int argc, char **argv) {

    if (argc < 2) {
        std::cerr << "Usage: bindump <filename>\n";
        return EXIT_FAILURE;
    }

    std::ifstream myfile(argv[1], std::ios::binary);

    if (!myfile.is_open())
    {
        std::cerr << "Cannot read file!\n";
    }
    else
    {
        unsigned i =0;
        char ch;
        while (myfile.get(ch)) {
            std::cout << std::bitset<8>(ch) << " ";
            ++i;
            if (i == 8) {
                i = 0;
                std::cout << "\n";
            }
        }
        std::cout << "\n";
    }
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Yup, this is what I wanted. Didn't really want a string. I wanted binary and this was the way. Thank you! – krose Oct 07 '20 at 16:03
1

when you convert val into bin, you are converting whatever number val is into a binary string. if you want that string to be 8 bits with leading 0's then just add a while loop after you create bin and before you output it

while(bin.length()<8){bin="0"+bin;}
  
smcrowley
  • 451
  • 3
  • 10
  • 1
    `bin = (std::string("00000000") + bin).substr(bin.length() - 8);` might or might not be a more efficient way to do this. – Ken Y-N Oct 07 '20 at 15:50
  • Thanks for the explanation. I didn't realize I was doing that. The accepted answer is a simpler implementation. But this was very helpful. – krose Oct 07 '20 at 16:42
0

Would something like this work?

#include <algorithm>
#include <bitset>
#include <fstream>
#include <iostream>
#include <iterator>

void show_binary(unsigned char c) {
  static auto count = 0u;
  std::cout << std::bitset<8>(c) << " ";
  if ((++count % 8u) == 0u) {
    std::cout << "\n";
    count = 0u;
  }
}

void dump(const char* path) {
  std::ifstream fp(path, std::ios::in | std::ios::binary);
  std::for_each(std::istreambuf_iterator<char>(fp),
                std::istreambuf_iterator<char>(), show_binary);
}

int main(int argc, char* argv[]) {
    std::for_each(argv + 1, argv + argc, dump);
}
Escualo
  • 40,844
  • 23
  • 87
  • 135