tags:

views:

451

answers:

5

Hi all. I'm getting data from a binary file, reading from file and writing in a vector of unsigned char. I can't edit it, because I'm using a external library.

But the data that I'm reading from file is a 16 bits image, and I'd like to put the data in a vector of unsigned short

Maybe I can do a cast for it?

Rgds.

+3  A: 
vector<unsigned char> a = ...;
vector<unsigned short> b(a.begin(), a.end());

But you want valarrays for simple data vectors.

Alex
I don't know if he wants his vector of n unsigned chars to turn into a vector of n unsigned shorts, or n/2 unsigned shorts. Looks like he's assuming that unsigned short is 2 bytes if he wants n/2 elements. But the question as stated is unclear.
Alok
+5  A: 

A generic approach (not bullet proof):

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

typedef unsigned char u8;
typedef unsigned short u16;

u16 combine_two_bytes(u8 a, u8 b) {
    return a | (b << 8);
}

template<typename InIter, typename OutIter, typename InT, typename OutT>
void combine_pairs(InIter in, InIter in_end, OutIter out, OutT (*func)(InT, InT)) {
    while(1) {
        if(in == in_end) {
            break;
        }

        InT &left = *in++;

        if(in == in_end) {
            break;
        }

        InT &right = *in++;

        *out++ = func(left, right);
    }
}

int main() {
    using namespace std;    // lazy

    u8 input[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    const size_t in_size = sizeof(input) / sizeof(*input);
    u16 output[in_size / 2];

    cout << "Original: ";
    copy(input, input + in_size, ostream_iterator<int>(cout, " "));
    cout << endl;

    combine_pairs(input, input + in_size, output, combine_two_bytes);

    cout << "Transformed: ";
    copy(output, output + in_size / 2, ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
}
strager
Very nice. One minor critique is that your code is limited to random access containers because of the statement "i += 2" in your for loop. If you change that to "std::advance(i, 2)" it will work for all container (and will continue to be constant time for random access iterators).
R Samuel Klatchko
@Klatchko, Thanks for your comment. You're right in that the code was limited to random-access iterators. However, even when using `std::advance` I had a check against `i + 1` which can't be done using all containers. I've reworked the code and hopefully it works better now (and is more type-safe).
strager
+1  A: 

Assuming the binary data in your file is in little-endian order, I would do this the simple way:

vector<unsigned char> a = ...;

std::vector<unsigned short> b;
b.reserve( a.size() / sizeof(unsigned short) );

for( std::vector<unsigned char>::const_iterator i=a.begin(); i!=a.end(); i+=2 )
{
    unsigned short shortValue = *(i+1);
    shortValue <<= 8;
    shortValue |= *i;
    b.push_back( shortValue );
}

If the data in your file is big-endian you will need to compose the short value the other way round. You should also guard against the number of entries in "a" not being a multiple of 2.

Bids
A: 

disclaimer: I don't have a compiler right now:

vector<unsigned char> vec = getVector();
vector<unsigned short> sv(reinterpret_cast<unsigned short*>(&vec[0]), 
                          reinterpret_cast<unsigned short*>(&vec[vec.size()]));
rlbond
No need to do the casts. Vector have a constructor that takes two iterators. As long as the value type of the iterator is convertible to the value type of the destination container the compiler will do the correct thing.
Martin York
@Martin York: except he doesn't want to convert the `char`s to `short`s, he wants them reinterpreted as shorts.
rlbond
@rlbond: That's debatable. It depends how you read the question. Its skewed that way because of the comment under the question.
Martin York
No debate actually: the data represents "a 16 bits image", i.e the only way to get back each word is to combine 2 bytes. Widening each byte to 16 bits won't help.
MSalters
+1  A: 

If you just want to convert from one type to the other then use the standard constructor. As long as the iterators value type is auto convertible to the destination vectors value type the compiler will do the auto conversion between the two types. Just use the standard constructor

#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<unsigned char>      a;
    a.push_back((unsigned char)12);
    a.push_back((unsigned char)13);
    a.push_back((unsigned char)14);

    std::vector<unsigned short>     b(a.begin(),a.end());

    // Print out the vector
    std::copy(b.begin(),b.end(),std::ostream_iterator<unsigned short>(std::cout,"\t"));
}

> g++ t.cpp
> ./a.out
12  13 14

If you actually want to convert two bytes into one then some work is required. But it depends if the input data is actually the same endianess as the machine you are on. If you know that it is the same endianess that you just need to cast the input type.

#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<unsigned char>      a;

    // Make sure that the size is correct.
    // ie. An Odd number indicates that something is not quite correct.
    //
    std::vector<unsigned short>     b(static_cast<unsigned short*>(&a[0]),
                                      static_cast<unsigned short*>(&a[a.size()]));

    // Print out the vector
    std::copy(b.begin(),b.end(),std::ostream_iterator<unsigned short>(std::cout,"\t"));
}

Alternatively if you actually need to combine two values into a single value where the endianess is not the same as the target architecture, you can write a special iterator. Something like this:

#include <Converter.h>

int main()
{
    std::vector<unsigned char>      a;

    // Make sure that the size is correct.
    // ie. An Odd number indicates that something is not quite correct.
    //
    std::vector<unsigned short>     b(make_Converter(a.begin()),make_Converter(a.end()));

    // Print out the vector
    std::copy(b.begin(),b.end(),std::ostream_iterator<unsigned short>(std::cout,"\t"));
}

Converter.h

#include <vector>
#include <iostream>
#include <iterator>

template<typename I>
struct Converter
{
    I   iterator;

    typedef typename std::input_iterator_tag                    iterator_category;
    typedef typename std::iterator_traits<I>::value_type        value_type;
    typedef typename std::iterator_traits<I>::difference_type   difference_type;
    typedef typename std::iterator_traits<I>::pointer           pointer;
    typedef typename std::iterator_traits<I>::reference         reference;

    Converter(I iter)
        :iterator(iter)
    {}

    Converter& operator++()
    {
        iterator++;
        return *this;
    }

    Converter operator++(int)
    {
        Converter   tmp(*this);
        this->operator++();

        return (tmp);
    }

    value_type operator*()
    {
        /*
         * The actual calculation done here will depend on the underlying hardware.
         */
        typename std::iterator_traits<I>::value_type val(*iterator);
        val << 8;
        iterator++;
        val |= (*iterator);

        return val;
    }

    bool operator!=(Converter const& rhs)
    {
        return iterator != rhs.iterator;
    }
};

template<typename I>
Converter<I> make_Converter(I iter)
{
    return Converter<I>(iter);
}
Martin York
+1 Everyone must get a downvote for no reason on SO :)
AraK