Reading binary data into std::string c++ - c++

I am trying to read data from binary file to an std::string.Here is what I have tried at first.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char const *argv[])
{
fstream file("output.bin" , ios::out | ios::binary | ios::in);
string my_str(5, '\0');
file.read(my_str.c_str(), 5);
cout << "String = " << my_str<< endl ;
}
And the compiler gave the error :
error: invalid conversion from ‘const char*’ to ‘std::basic_istream<char>::char_type* {aka char*}’ [-fpermissive]
file.read(my_str.c_str(), 5);
As far as I understand, c_str() returns a const pointer which cannot be used in read method, so I changed my approach a little bit(which you can see below). Is there a better way to do this ?
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char const *argv[])
{
fstream file("output.bin" , ios::out | ios::binary | ios::in);
string my_str(5, '\0');
char buffer[6];
file.read(buffer, 5);
buffer[5] = '\0';
my_str = string(buffer);
cout << "String = " << my_str<< endl ;
}
ps : forgive me if I could not make myself clear, this is my first time here :)

In C++11, the way to get a non-const pointer to the string's data is:
file.read(&my_str[0], 5);
C++17 will introduce non-const data() for this as well:
file.read(my_str.data(), 5);

another way, using standard algorithms:
#include <iostream>
#include <string>
#include <fstream>
#include <algorithm>
#include <iterator>
using namespace std;
int main(int argc, char const *argv[])
{
fstream file("output.bin" , ios::out | ios::binary | ios::in);
auto my_str = string();
copy_n(istream_iterator<char>(file),
5,
std::back_inserter(my_str));
cout << "String = " << my_str<< endl ;
}

std::string is specially designed to work with strings and with c-style strings as well, so this fact will work against you in this situation. For example your code:
char buffer[6];
file.read(buffer, 5);
buffer[5] = '\0';
my_str = string(buffer);
what is wrong with it? You are reading binary data and who guarantees that there won't be '\0' byte there? You can fix it by:
my_str = string(buffer,5);
but this shows the point - std::string as a buffer is not a good choice. So you better use std::vector<char> or even better std::vector<uint8_t> which has method data() but will not implicitly convert from c-string, output to std::ostream etc.

Related

yaml-cpp error while converting Node to a std::string

I want to convert a Node object from yaml-cpp to a std::string. I keep getting an error that says it cannot find the right overloaded operator <<.
Here's my code:
#include <iostream>
#include <string>
#include <yaml-cpp/yaml.h>
using namespace std;
int main(int argc, char* argv[]) {
YAML::Node myNode;
myNode["hello"] = "world";
cout << myNode << endl; // That works but I want that not to stdout
string myString = "";
myNode >> myString; // Error at this line
cout << myString << endl;
return 0;
}
The error is
src/main.cpp:13:12: error: invalid operands to binary expression ('YAML::Node' and 'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char>>')
[somefile] note: candidate function template not viable: no known conversion from 'YAML::Node' to 'std::byte' for 1st argument
operator>> (byte __lhs, _Integer __shift) noexcept
^
[A bunch of these 10+]
Using clang++ -std=c++20 -I./include -lyaml-cpp -o prog main.cpp
Please help me find out!
Actually needed to create an stringstream and then convert it to an normal string:
#include <iostream>
#include <string>
#include <yaml-cpp/yaml.h>
using namespace std;
int main(int argc, char* argv[]) {
YAML::Node myNode;
myNode["hello"] = "world";
stringstream myStringStream;
myStringStream << myNode;
string result = myStringStream.str();
cout << result << end;
return 0;
}
Output:
hello: world

How to read hex values into integer with fstream (C++)

I am trying to read a little-endian hex string from a binary file, and put that value into an integer to work with it. When I try to read, instead of getting a number I get ascii symbols. I've tried casts and atoi and nothing seems to work. What is the best way to use fstream to read a hex string into an integer from a file?
This is essentially my program:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
fstream input;
fstream output;
char cbuffer[4];
char revbuffer[8];
input.open(argv[1], fstream::binary | fstream::in);
output.open("output.txt", ios::out | ios::app);
input.seekg(16, input.beg);
input.read(cbuffer, 4);
cout << sizeof(revbuffer) << endl;
cout << cbuffer[0] << cbuffer[1] << cbuffer[2] << cbuffer[3] << endl;
}
If it's an integer value stored in binary format, I guess it's either a int32_t or a uint32_t. Since you mention that the value is stored in little-endian byte order, I guess you want to make sure that the host running your program converts it (if it needs to). C++20 has std::endian. If that's not available to you, there are usually macros for detecting endianness at compiletime that you can use instead of the std::endian tests I've used. I've assumed that the value is a uint32_t below.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <type_traits> // std::endian
// little endian unsigned 32 bit integer to host byte order
inline uint32_t Le32toh(uint32_t le) {
#if __cplusplus <= 201703L
// run-time check
static constexpr uint16_t endian = 1;
if(*reinterpret_cast<const uint8_t*>(&endian)==1) return le;
#else
// compile-time check
static_assert(std::endian::native == std::endian::little || std::endian::native == std::endian::big);
if constexpr (std::endian::native == std::endian::little) return le;
#endif
const uint8_t* c=reinterpret_cast<const uint8_t*>(&le);
return // little-to-big endian conversion
(static_cast<uint32_t>(c[0])<<24) |
(static_cast<uint32_t>(c[1])<<16) |
(static_cast<uint32_t>(c[2])<<8) |
(static_cast<uint32_t>(c[3]));
return le;
}
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv+1, argv+argc);
std::fstream output("output.txt", std::ios::out | std::ios::app);
uint32_t cbuffer;
for(const auto& file : args) {
std::fstream input(file, std::fstream::binary | std::fstream::in);
input.seekg(16, input.beg);
// read directly into the varibles memory
input.read(reinterpret_cast<char*>(&cbuffer), 4);
// output the value unconverted
std::cout << std::hex << cbuffer << "\n";
// convert if needed
cbuffer = Le32toh(cbuffer);
// output the value converted
std::cout << std::hex << cbuffer << "\n";
}
}

Storing std::string as character array

I'm trying to convert an std::string into a c string (my real problem is closer to this; I'm trying a workaround).
How does one store an std::string as a character array?
Why do std::string.data() and std::string.c_str() return char [(<anonymous> + 1)] instead of the expected const char*? Or do I have these mixed up, and if so, how do I work with these types?
#include <string>
#include <iostream>
int main()
{
std::string sstring = "I'm an std string!";
char cstring[sstring.size()];
cstring = sstring.c_str();
std::cout << std::string(sstring.c_str());
return 0;
}
Compilation results in
scratch.cpp:10:11: error: incompatible types in assignment of ‘const char*’ to ‘char [(<anonymous> + 1)]’
I wasn't able to find a relevant, preexisting question.
Just use a const char *
std::string sstring = "I'm an std string!";
const char * cstring = sstring.c_str();
std::cout << cstring << std::endl;
As mentioned in the comments, this sound like an XY problem. But if you simply want to copy the string you can do it like so:
char cstring[sstring.size() + 1];
strcpy(cstring, sstring.c_str());
Just be aware that VLAs are not a part of C++ but g++ will 'go with it', see answers here.
A working way to assign your string to a char array is the following:
#include <iostream>
#include <string>
int main(int argc, char **argv) {
std::string s = "HELLO WORLD!";
char *cs = (char *)s.c_str();
std::cout << cs << '\n' << std::string(cs) << '\n';
return 0;
}
OUTPUT:
This works because it casts the const char * to char * and therefore allows the assignment to go through, and although this may not be the best way it is very simple.

Cannot push C style strings into std::vector

I'm trying to push some const char* into a vector, but the vector remains unpopulated after performing the operations I would presume to fill it.
Here's my attempt, where dict is my command-line argument.
test.cc
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
int main(int argc, char **argv)
{
ifstream dict;
size_t dict_size;
dict.open(argv[1]); // Dictionary
vector<const char*> dictionary;
string line;
getline(dict, line);
while(!dict.fail()) {
dictionary.push_back(line.c_str());
getline(dict, line);
}
dict_size = dictionary.size();
for(int i = 0; i < dict_size; i++)
cout << "dictionary[" << i << "] is " << dictionary[i] << endl;
}
dict
Hello
World
Foo
Bar
After compiling this, I get the following output:
dictionary[0] is
dictionary[1] is
dictionary[2] is
dictionary[3] is
However, if I change the dictionary's type to vector and push back line instead of line.c_str(), I get the expected output:
dictionary[0] is Hello
dictionary[1] is World
dictionary[2] is Foo
dictionary[3] is Bar
I'm not terribly familiar with C style strings, so maybe it has something to do with null termination?
You are storing dangling pointers.
std::string::c_str() isn't a pointer to some permanent copy of data — just think, that would be leaked!
Store the std::strings instead.
Your code invokes undefined behavior, because after you do
dictionary.push_back(line.c_str());
On the next line that pointer may get deleted:
getline(dict, line); // line now is a different string
You are pushing into the dictionary pointers that point to the same address and at the last iteration it fills the memory area with an empty string. If you don't care about memory leakage you can try like this:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
int main(int argc, char **argv)
{
ifstream dict;
size_t dict_size;
dict.open(argv[1]); // Dictionary
vector<char *> dictionary;
while(!dict.fail()) {
string * line = new string();
getline(dict, *line);
if(line->length()>0)
{
dictionary.push_back((char *)line->c_str());
}
}
dict_size = dictionary.size();
for(int i = 0; i < dict_size; i++)
cout << "dictionary[" << i << "] is " << dictionary[i] << endl;
}

I am unable to store string into an array, which i then want to use for comparison

I am unable to store the string into an array, when I output b[c] nothing appears whatsoever, how can I store it into an array?
int main(int argc, char *argv[])
{
string b[80000];
int c=0;
string s;
ifstream file(argv[1]);
while(file >> s) {
b[c]=s;
c++;
cout<<b[c];
}
system("pause");
return 0;
}
You're printing empty strings. Just move cout << b[c]; before c++;
I'd suggest use std::vector, it will avoid unnecessary temporary variables and magic constants:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
int main(int argc, const char* argv[])
{
std::ifstream fin(argv[1]);
std::vector<std::string> v
{
std::istream_iterator<std::string>(fin),
std::istream_iterator<std::string>()
};
for(const auto& elem: v)
std::cout << elem << std::endl;
return 0;
}
Don't forget also handle cases when file name isn't passed, or file doesn't exist.
This may work
while(file >> s) {
b[c]=s;
cout<<b[c];
c++;
}