Writing C++ program compressing/decompressing data - c++

I have to write C++ program that like gzip can
*Take input from file or from char stream like compression below
gzip file
type file | gzip
*Program have file or char stream output like decompression below
gzip -d file.gz
gzip -dc file.gz
I don't know how to take to the task and what techniques have to use and how to create classes buffering input and output. I have classes buffering input and output and read/write data from/to file.
DataBuffer.h (taking uncompressed data from file):
#ifndef DataBuffer_h
#define DataBuffer_h
#include <fstream>
#include <string>
enum DataBufferState
{
DATABUFFER_OK = 0,
DATABUFFER_EOF = 1
};
class DataBuffer
{
std::fstream file;
std::string buffer;
unsigned int maxBufferSize;
public:
DataBuffer(const std::string& filename, unsigned int maxBuffSize);
~DataBuffer();
bool OpenFile(const std::string& filename);
void SetMaxBufferSize(unsigned int maxBuffSize);
DataBufferState FullBufferWithDataOld();
DataBufferState FullBufferWithData();
std::string GetDataBuffer();
};
#endif
DataBuffer.cpp:
#include "DataBuffer.h"
using namespace std;
DataBuffer::DataBuffer(const string& filename, unsigned int maxBuffSize)
{
OpenFile(filename);
SetMaxBufferSize(maxBuffSize);
}
DataBuffer::~DataBuffer()
{
file.close();
}
bool DataBuffer::OpenFile(const string& filename)
{
file.open(filename.c_str(),ios::in);
if(!file.is_open())
return false;
return true;
}
void DataBuffer::SetMaxBufferSize(unsigned int maxBuffSize)
{
maxBufferSize = maxBuffSize;
}
DataBufferState DataBuffer::FullBufferWithDataOld()
{
while(true)
{
string line;
streampos pos = file.tellg(); // Zapamietaj polozenie przed pobraniem linii
getline(file,line);
if( buffer.size()+line.size()>maxBufferSize )
{
// Cofnac wskaznik pliku
file.seekg(pos,ios::beg); // Przywroc polozenie sprzed pobrania linii
break;
}
buffer += line + "\n";
if(file.eof())
return DATABUFFER_EOF;
}
return DATABUFFER_OK;
}
DataBufferState DataBuffer::FullBufferWithData()
{
char c;
for(unsigned int i=0;i<maxBufferSize;++i)
{
c = file.get();
if(file.eof()) break;
buffer += c;
}
if(file.eof())
return DATABUFFER_EOF;
return DATABUFFER_OK;
}
string DataBuffer::GetDataBuffer()
{
string buf = buffer;
buffer.clear();
return buf;
}
BufferWriter.h (Save uncompressed data into file):
#ifndef BufferWriter_h
#define BufferWriter_h
#include <string>
#include <fstream>
class BufferWriter
{
std::string filename;
std::fstream file;
public:
BufferWriter(const std::string& filename_);
~BufferWriter();
bool OpenFile(const std::string& filename, bool appending);
void SendBufferToFile(std::string& buffer);
};
#endif
BufferWriter.cpp
#include "BufferWriter.h"
using namespace std;
BufferWriter::BufferWriter(const string& filename_)
{
filename = filename_;
OpenFile(filename.c_str(),false);
file.close();
}
BufferWriter::~BufferWriter()
{
file.close();
}
bool BufferWriter::OpenFile(const string& filename, bool appending)
{
if(appending)
file.open(filename.c_str(),ios::out | ios::app);
else
file.open(filename.c_str(),ios::out);
if(!file.is_open())
return false;
return true;
}
void BufferWriter::SendBufferToFile(string& buffer)
{
OpenFile(filename,true);
file.write(buffer.c_str(),buffer.size());
file.close();
}
Can you give me some hints how to improve code for input and output mechanisms?
Assume that I have class presented below, how to use istream or iterators to fill buffer with data from file or standard input. What classes from std or boost? What parameters? Somelike to support definition of class with this functionality.
[EDIT]:
#ifndef StreamBuffer_h
#define StreamBuffer_h
#include <string>
using namespace std;
enum DataBufferState
{
DATABUFFER_OK = 0,
DATABUFFER_EOF = 1
};
// gzip plik
// type plik | gzip -d
// gzip -d plik.gz
// gzip -dc plik.gz
// Parametr konstruktora to strumien z ktorego chcemy czytac i dlugosc bufora
class StreamBuffer
{
int maxBufferSize;
std::string buffer;
StreamBuffer(int maxBuffSize)
{
SetMaxBufferSize(maxBuffSize);
}
~StreamBuffer()
{
}
void SetMaxBufferSize(unsigned int maxBuffSize)
{
maxBufferSize = maxBuffSize;
}
DataBufferState FullBufferWithData()
{
// What to use what to do in this method to read part of file or standard char input to buffer?
}
std::string GetDataBuffer()
{
return buffer;
}
};
#endif
[EDIT2]:
I want to do the same thing as in this thread: Read from file or stdin, but in C++.

In general you read input from a source and write it to a sink. The simplest case is when you simply write what you read. You, however, want to apply a transformation (or filter) to the data that you read. Seeing as you're after "the c++ way," I'd suggest taking a look at boost::iostreams which abstracts the task in terms of sources/sinks.
Boost defines an abstract source by:
struct Source {
typedef char char_type;
typedef source_tag category;
std::streamsize read(char* s, std::streamsize n)
{
// Read up to n characters from the input
// sequence into the buffer s, returning
// the number of characters read, or -1
// to indicate end-of-sequence.
}
};
And sinks are defined in a similar way (with a write instead of a read, of course). The benefit of this is that the details of the source/sink is irrelevant - you can read/write to file, to a network adapter, or whatever, without any structural changes.
To apply filters I'd again suggest looking at boost::iostreams, although they do abstract a lot which somewhat complicates implementation..

Related

C++ OpenSSL hash of the file is not the right one

I want to calculate Sha1 of any given file in C++ using OpenSSL library.
I have read any article on the internet (including all from stackoverflow too) about doing this for almost 3 days.
Finally I get my program to work but the generated hash of any given file is not as it should be.
My code is someway similar to these found here and here but more easy to read and to use further in my program I write.
Also, I want to use C++ code not C code as they are written in the links above, second, they use:
SHA256_Init(&context);
SHA256_Update(&context, (unsigned char*)input, length);
SHA256_Final(md, &context);
which aren't available anymore in the new/current OpenSSL version (3.0 or so, I think).
So, I think this question will help many other readers that I observe meet the same problem(s) I do with the new OpenSSL version and can not use old code samples anymore.
This is my C++ code that is created to read huge files by chuncks without loading them into memory (hope this will help future readers of this post because it have many useful lines but it is not fully working as you will see):
bool hashFullFile(const std::string& FilePath, std::string &hashed, std::string &hash_type) {
bool success = false;
EVP_MD_CTX *context = EVP_MD_CTX_new();
//read file by chuncks:
const int BUFFER_SIZE = 1024;
std::vector<char> buffer (BUFFER_SIZE + 1, 0);
// check if the file to read from exists and if so read the file in chunks
std::ifstream fin(FilePath, std::ifstream::binary | std::ifstream::in);
if (hash_type == "SHA1") {
if (context != NULL) {
if (EVP_DigestInit_ex(context, EVP_sha1(), NULL)) {
while (fin.good()){
fin.read(buffer.data(), BUFFER_SIZE);
std::streamsize s = ((fin) ? BUFFER_SIZE : fin.gcount());
buffer[s] = 0;
//convert vector of chars to string:
std::string str(buffer.data());
if (!EVP_DigestUpdate(context, str.c_str(), str.length())) {
fprintf(stderr, "Error while digesting file.\n");
return false;
}
}
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int lengthOfHash = 0;
if (EVP_DigestFinal_ex(context, hash, &lengthOfHash)) {
std::stringstream ss;
for (unsigned int i = 0; i < lengthOfHash; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (int) hash[i];
}
hashed = ss.str();
success = true;
}else{
fprintf(stderr, "Error while finalizing digest.\n");
return false;
}
}else{
fprintf(stderr, "Error while initializing digest context.\n");
return false;
}
EVP_MD_CTX_free(context);
}else{
fprintf(stderr, "Error while creating digest context.\n");
return false;
}
}
fin.close();
return success;
}
And I am using it like this into main function:
std::string myhash;
std::string myhash_type = "SHA1";
hashFullFile(R"(C:\Users\UserName\data.bin)", myhash, myhash_type);
cout<<myhash<<endl;
The problem is that for a given file it calculates hash:
e.g. 169ed28c9796a8065f96c98d205f21ddac11b14e as the hash output but the same file has the hash:
openssl dgst -sha1 data.bin
SHA1(data.bin)= 1927f720a858d0c3b53893695879ae2a7897eedb
generated by Openssl command line and also by any site from the internet.
I can't figure out what am I doing wrong since my code seems to be correct.
Please help.
Thank you very much in advance!
You're missing the finishing calculation on your EVP API attempt. The use of an intermediate string is unnecessary as well. Finally, the function should return the digest as a vector of bytes. let the caller do with that what they want.
Examples using both the EVP API and a BIO chain are shown below.
#include <iostream>
#include <fstream>
#include <algorithm>
#include <array>
#include <vector>
#include <memory>
#include <openssl/evp.h>
#include <openssl/sha.h>
namespace
{
struct Delete
{
void operator()(BIO * p) const
{
BIO_free(p);
}
void operator()(EVP_MD_CTX *p) const
{
EVP_MD_CTX_free(p);
}
};
using BIO_ptr = std::unique_ptr<BIO, Delete>;
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, Delete>;
}
std::vector<uint8_t> hashFileEVP(const std::string &fname, std::string const &mdname = "sha1")
{
// will hold the resulting digest
std::vector<uint8_t> md;
// set this to however big you want the chunk size to be
static constexpr size_t BUFFER_SIZE = 1024;
std::array<char, BUFFER_SIZE> buff;
// get the digest algorithm by name
const EVP_MD *mthd = EVP_get_digestbyname(mdname.c_str());
if (mthd)
{
std::ifstream inp(fname, std::ios::in | std::ios::binary);
if (inp.is_open())
{
EVP_MD_CTX_ptr ctx{EVP_MD_CTX_new()};
EVP_DigestInit_ex(ctx.get(), mthd, nullptr);
while (inp.read(buff.data(), BUFFER_SIZE).gcount() > 0)
EVP_DigestUpdate(ctx.get(), buff.data(), inp.gcount());
// size output vector
unsigned int mdlen = EVP_MD_size(mthd);
md.resize(mdlen);
// general final digest
EVP_DigestFinal_ex(ctx.get(), md.data(), &mdlen);
}
}
return md;
}
std::vector<uint8_t> hashFileBIO(std::string const &fname, std::string const &mdname = "sha1")
{
// the fixed-size read buffer
static constexpr size_t BUFFER_SIZE = 1024;
// will hold the resulting digest
std::vector<uint8_t> md;
// select this however you want.
const EVP_MD *mthd = EVP_get_digestbyname(mdname.c_str());
if (mthd)
{
// open the file and a message digest BIO
BIO_ptr bio_f(BIO_new_file(fname.c_str(), "rb"));
BIO_ptr bio_md(BIO_new(BIO_f_md()));
BIO_set_md(bio_md.get(), mthd);
// chain the bios together. note this bio is NOT
// held together with a smart pointer; all the
// bios in the chain are.
BIO *bio = BIO_push(bio_md.get(), bio_f.get());
// read through file one buffer at a time.
std::array<char, BUFFER_SIZE> buff;
while (BIO_read(bio, buff.data(), buff.size()) > 0)
; // intentionally empty
// size output buffer
unsigned int mdlen = EVP_MD_size(mthd);
md.resize(mdlen);
// read final digest from md bio.
BIO_gets(bio_md.get(), (char *)md.data(), mdlen);
}
return md;
}
// convert a vector of byte to std::string
std::string bin2hex(std::vector<uint8_t> const& bin)
{
std::string res;
size_t len = 0;
if (OPENSSL_buf2hexstr_ex(nullptr, 0, &len, bin.data(), bin.size(), 0) != 0)
{
res.resize(len);
OPENSSL_buf2hexstr_ex(&res[0], len, &len, bin.data(), bin.size(), 0);
}
return res;
}
int main()
{
OpenSSL_add_all_digests();
// i have this on my rig. use whatever you want
// or get the name from argv or some such.
static const char fname[] = "dictionary.txt";
auto md1 = hashFileEVP(fname);
auto md1str = bin2hex(md1);
std::cout << "hashed with EVP API\n";
std::cout << md1str << '\n';
auto md2 = hashFileBIO(fname);
auto md2str = bin2hex(md1);
std::cout << "hashed with BIO chain\n";
std::cout << md2str << '\n';
}
Output
hashed with EVP API
0A97D663ADA2E039FD904846ABC5361291BD2D8E
hashed with BIO chain
0A97D663ADA2E039FD904846ABC5361291BD2D8E
Output from openssl command line
craig#rogue1 % openssl dgst -sha1 dictionary.txt
SHA1(dictionary.txt)= 0a97d663ada2e039fd904846abc5361291bd2d8e
Note the digests are the same in all three cases.

Can't read file with cyrillic path in C++

I'm trying to read file, which contains Cyrillic characters in their path, and got ifstream.is_open() == false
This is my code:
std::string ReadFile(const std::string &path) {
std::string newLine, fileContent;
std::ifstream in(path.c_str(), std::ios::in);
if (!in.is_open()) {
return std::string("isn't opened");
}
while (in.good()) {
getline(in, newLine);
fileContent += newLine;
}
in.close();
return fileContent;
}
int main() {
std::string path = "C:\\test\\документ.txt";
std::string content = ReadFile(path);
std::cout << content << std::endl;
return 0;
}
Specified file exists
I'm trying to find solution in google, but I got nothing
Here is links, which I saw:
I don't need wstring
The same as previous
no answer here
is not about C++
has no answer too
P.S. I need to get file's content in string, not in wstring
THIS IS ENCODING SETTINGS OF MY IDE (CLION 2017.1)
You'll need an up-to-date compiler or Boost. std::filesystem::path can handle these names, but it's new in the C++17 standard. Your compiler may still have it as std::experimental::filesystem::path, or else you'd use the third-party boost::filesystem::path. The interfaces are pretty comparable as the Boost version served as the inspiration.
The definition for std::string is std::basic_string, so your Cyrillic chararecters are not stored as intended. Atleast, try to use std::wstring to store your file path and then you can read from file using std::string.
First of all, set your project settings to use UTF-8 encoding instead of windows-1251. Until standard library gets really good (not any time soon) you basically can not rely on it if you want to deal with io properly. To make input stream read from files on Windows you need to write your own custom input stream buffer that opens files using 2-byte wide chars or rely on some third-party implementations of such routines. Here is some incomplete (but sufficient for your example) implementation:
// assuming that usual Windows SDK macros such as _UNICODE, WIN32_LEAN_AND_MEAN are defined above
#include <Windows.h>
#include <string>
#include <iostream>
#include <system_error>
#include <memory>
#include <utility>
#include <cstdlib>
#include <cstdio>
static_assert(2 == sizeof(wchar_t), "wchar_t size must be 2 bytes");
using namespace ::std;
class MyStreamBuf final: public streambuf
{
#pragma region Fields
private: ::HANDLE const m_file_handle;
private: char m_buffer; // typically buffer should be much bigger
#pragma endregion
public: explicit
MyStreamBuf(wchar_t const * psz_file_path)
: m_file_handle(::CreateFileW(psz_file_path, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))
, m_buffer{}
{
if(INVALID_HANDLE_VALUE == m_file_handle)
{
auto const error_code{::GetLastError()};
throw(system_error(static_cast< int >(error_code), system_category(), "::CreateFileW call failed"));
}
}
public:
~MyStreamBuf(void)
{
auto const closed{::CloseHandle(m_file_handle)};
if(FALSE == closed)
{
auto const error_code{::GetLastError()};
//throw(::std::system_error(static_cast< int >(error_code), system_category(), "::CloseHandle call failed"));
// throwing in destructor is kinda wrong
// but if CloseHandle returned false then our program is in inconsistent state
// and must be terminated anyway
(void) error_code; // not used
abort();
}
}
private: auto
underflow(void) -> int_type override
{
::DWORD bytes_count_to_read{1};
::DWORD read_bytes_count{};
{
auto const succeeded{::ReadFile(m_file_handle, addressof(m_buffer), bytes_count_to_read, addressof(read_bytes_count), nullptr)};
if(FALSE == succeeded)
{
auto const error_code{::GetLastError()};
setg(nullptr, nullptr, nullptr);
throw(system_error(static_cast< int >(error_code), system_category(), "::ReadFile call failed"));
}
}
if(0 == read_bytes_count)
{
setg(nullptr, nullptr, nullptr);
return(EOF);
}
setg(addressof(m_buffer), addressof(m_buffer), addressof(m_buffer) + 1);
return(m_buffer);
}
};
string
MyReadFile(wchar_t const * psz_file_path)
{
istream in(new MyStreamBuf(psz_file_path)); // note that we create normal stream
string new_line;
string file_content;
while(in.good())
{
getline(in, new_line);
file_content += new_line;
}
return(::std::move(file_content));
}
int
main(void)
{
string content = MyReadFile(L"C:\\test\\документ.txt"); // note that path is a wide string
cout << content << endl;
return 0;
}
Change your code to use wstring and save your file using Unicode encoding (non UTF8 one, use USC-2, UTF16 or something like that). MSVC has non-standard overload specifically for this reason to be able to handle non-ascii chars in filenames:
std::string ReadFile(const std::wstring &path)
{
std::string newLine, fileContent;
std::ifstream in(path.c_str(), std::ios::in);
if (!in)
return std::string("isn't opened");
while (getline(in, newLine))
fileContent += newLine;
return fileContent;
}
int main()
{
std::wstring path = L"C:\\test\\документ.txt";
std::string content = ReadFile(path);
std::cout << content << std::endl;
}
Also, note corrected ReadFile code.

istream object don't read any char

Why istream object after calling readsome() method don't give any chars in buffer? Is there any error in class construction?
StreamBuffer.h
#ifndef StreamBuffer_h
#define StreamBuffer_h
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
enum StreamBufferState
{
STREAMBUFFER_OK = 0,
STREAMBUFFER_EOF = 1
};
class StreamBuffer
{
std::fstream file;
std::istream istrm;
int maxBufferSize;
std::string buffer;
public:
StreamBuffer(int maxBuffSize, const std::string& filename);
~StreamBuffer();
void SetMaxBufferSize(unsigned int maxBuffSize);
StreamBufferState FullBufferWithData();
std::string GetDataBuffer();
};
#endif
StreamBuffer.cpp
#include "StreamBuffer.h"
using namespace std;
StreamBuffer::StreamBuffer(int maxBuffSize, const std::string& filename) : istrm( !filename.empty() ? file.rdbuf() : cin.rdbuf() )
{
SetMaxBufferSize(maxBuffSize);
if(!filename.empty())
{
file.open(filename.c_str(),ios::in | ios::binary);
}
else
{
std::cin>>noskipws;
}
}
StreamBuffer::~StreamBuffer()
{
file.close();
}
void StreamBuffer::SetMaxBufferSize(unsigned int maxBuffSize)
{
maxBufferSize = maxBuffSize;
}
StreamBufferState StreamBuffer::FullBufferWithData()
{
istrm.readsome((char*)&buffer[0],maxBufferSize);
if(istrm.eof())
return STREAMBUFFER_EOF;
return STREAMBUFFER_OK;
}
std::string StreamBuffer::GetDataBuffer()
{
string buf = buffer;
return buf;
}
File is opened, but readsome() don't read buffer.
You have undefined behavior in your code, as you try read into an empty string. You need to set the size of buffer.
An unrelated logical error: In the FullBufferWithData function you will return "OK" even if there is an error reading the file.

Handle socket descriptors like file descriptor (fstream)? C++/Linux

By accident I found out that I can use read and write on socket descriptors. Can I somehow (ab)use the fstream mechanism to output data into the socket descriptor?
The standard file stream doesn't support use of a file descriptor. However, the I/O stream classes make it reasonably easy to create your own abstraction which allows creating your own sources of or destination for characters. The magic class is std::streambuf whose responsibility is to buffer characters and read or write characters at appropriate times. Nicolai Josuttis's "The C++ Standard Library" has a detailed description of how to do so (the basis of which I contributed to Nico many years ago). A simple implementation of a stream buffer using a socket for reading and writing would look something like this:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <streambuf>
#include <cstddef>
#include <unistd.h>
class fdbuf
: public std::streambuf
{
private:
enum { bufsize = 1024 };
char outbuf_[bufsize];
char inbuf_[bufsize + 16 - sizeof(int)];
int fd_;
public:
typedef std::streambuf::traits_type traits_type;
fdbuf(int fd);
~fdbuf();
void open(int fd);
void close();
protected:
int overflow(int c);
int underflow();
int sync();
};
fdbuf::fdbuf(int fd)
: fd_(-1) {
this->open(fd);
}
fdbuf::~fdbuf() {
this->close();
}
void fdbuf::open(int fd) {
this->close();
this->fd_ = fd;
this->setg(this->inbuf_, this->inbuf_, this->inbuf_);
this->setp(this->outbuf_, this->outbuf_ + bufsize - 1);
}
void fdbuf::close() {
if (!(this->fd_ < 0)) {
this->sync();
::close(this->fd_);
}
}
int fdbuf::overflow(int c) {
if (!traits_type::eq_int_type(c, traits_type::eof())) {
*this->pptr() = traits_type::to_char_type(c);
this->pbump(1);
}
return this->sync() == -1
? traits_type::eof()
: traits_type::not_eof(c);
}
int fdbuf::sync() {
if (this->pbase() != this->pptr()) {
std::streamsize size(this->pptr() - this->pbase());
std::streamsize done(::write(this->fd_, this->outbuf_, size));
// The code below assumes that it is success if the stream made
// some progress. Depending on the needs it may be more
// reasonable to consider it a success only if it managed to
// write the entire buffer and, e.g., loop a couple of times
// to try achieving this success.
if (0 < done) {
std::copy(this->pbase() + done, this->pptr(), this->pbase());
this->setp(this->pbase(), this->epptr());
this->pbump(size - done);
}
}
return this->pptr() != this->epptr()? 0: -1;
}
int fdbuf::underflow()
{
if (this->gptr() == this->egptr()) {
std::streamsize pback(std::min(this->gptr() - this->eback(),
std::ptrdiff_t(16 - sizeof(int))));
std::copy(this->egptr() - pback, this->egptr(), this->eback());
int done(::read(this->fd_, this->eback() + pback, bufsize));
this->setg(this->eback(),
this->eback() + pback,
this->eback() + pback + std::max(0, done));
}
return this->gptr() == this->egptr()
? traits_type::eof()
: traits_type::to_int_type(*this->gptr());
}
int main()
{
fdbuf inbuf(0);
std::istream in(&inbuf);
fdbuf outbuf(1);
std::ostream out(&outbuf);
std::copy(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(out));
}

How to copy the output of linux command to a C++ variable

I'm calling a LINUX command from within a C++ programme which creates the following output. I need to copy the first column of the output to a C++ variable (say a long int). How can I do it?? If that is not possible how can I copy this result into a .txt file with which I can work with?
Edit
0 +0
2361294848 +2361294848
2411626496 +50331648
2545844224 +134217728
2713616384 +167772160
I have this stored as a file, file.txt and I'm using the following code to
extract the left column with out the 0 to store it at integers
string stringy="";
int can_can=0;
for(i=begin;i<length;i++)
{
if (buffer[i]==' ' && can_can ==1) //**buffer** is the whole text file read in char*
{
num=atoi(stringy.c_str());
array[univ]=num; // This where I store the values.
univ+=1;
can_can=1;
}
else if (buffer[i]==' ' && can_can ==0)
{
stringy="";
}
else if (buffer[i]=='+')
{can_can=0;}
else{stringy.append(buffer[i]);}
}
I'm getting a segmentation error for this. What can be done ?
Thanks in advance.
Just create a simple streambuf wrapper around popen()
#include <iostream>
#include <stdio.h>
struct SimpleBuffer: public std::streambuf
{
typedef std::streambuf::traits_type traits;
typedef traits::int_type int_type;
SimpleBuffer(std::string const& command)
: stream(popen(command.c_str(), "r"))
{
this->setg(&c[0], &c[0], &c[0]);
this->setp(0, 0);
}
~SimpleBuffer()
{
if (stream != NULL)
{
fclose(stream);
}
}
virtual int_type underflow()
{
std::size_t size = fread(c, 1, 100, stream);
this->setg(&c[0], &c[0], &c[size]);
return size == 0 ? EOF : *c;
}
private:
FILE* stream;
char c[100];
};
Usage:
int main()
{
SimpleBuffer buffer("echo 55 hi there Loki");
std::istream command(&buffer);
int value;
command >> value;
std::string line;
std::getline(command, line);
std::cout << "Got int(" << value << ") String (" << line << ")\n";
}
Result:
> ./a.out
Got int(55) String ( hi there Loki)
It is popen you're probably looking for. Try
man popen
.
Or see this little example:
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
FILE *in;
char buff[512];
if(!(in = popen("my_script_from_command_line", "r"))){
return 1;
}
while(fgets(buff, sizeof(buff), in)!=NULL){
cout << buff; // here you have each line
// of the output of your script in buff
}
pclose(in);
return 0;
}
Unfortunately, it’s not easy since the platform API is written for C. The following is a simple working example:
#include <cstdio>
#include <iostream>
int main() {
char const* command = "ls -l";
FILE* fpipe = popen(command, "r");
if (not fpipe) {
std::cerr << "Unable to execute commmand\n";
return EXIT_FAILURE;
}
char buffer[256];
while (std::fgets(buffer, sizeof buffer, fpipe)) {
std::cout << buffer;
}
pclose(fpipe);
}
However, I’d suggest wrapping the FILE* handle in a RAII class to take care of resource management.
You probably want to use popen to execute the command. This will give you a FILE * that you can read its output from. From there, you can parse out the first number with (for example) something like:
fscanf(inpipe, "%d %*d", &first_num);
which, just like when reading from a file, you'll normally repeat until you receive an end of file indication, such as:
long total = 0;
while (1 == fscanf(inpipe, "%l %*d", &first_num))
total = first_num;
printf("%l\n", total);