C# ushort[] to string conversion; is this possible?

11,030

Solution 1

P/Invoke can actually handle what you're after most of the time using StringBuilder to create writable buffers, for example see pinvoke.net on GetWindowText and related functions.

However, that aside, with data as ushort, I assume that it is encoded in UTF-16LE. If that is the case you can use Encoding.Unicode.GetString(), but that will exepect a byte array rather than a ushort array. To turn your ushorts into bytes, you can allocate a separate byte array and use Buffer.BlockCopy, something like this:

ushort[] data = new ushort[10];
for (int i = 0; i < data.Length; ++i)
    data[i] = (char) ('A' + i);

string asString;
byte[] asBytes = new byte[data.Length * sizeof(ushort)];
Buffer.BlockCopy(data, 0, asBytes, 0, asBytes.Length);
asString = Encoding.Unicode.GetString(asBytes);

However, if unsafe code is OK, you have another option. Get the start of the array as a ushort*, and hard-cast it to char*, and then pass it to the string constructor, like so:

string asString;
unsafe
{
    fixed (ushort *dataPtr = &data[0])
        asString = new string((char *) dataPtr, 0, data.Length);
}

Solution 2

One thing you can do is switch from using a string to a stringBuilder it will help performance tremendously.

If you are willing to use unsafe code you can use pointers and implement the your c# code just like your c++. Or you could write a small c++\cli dll that implements this functionality.

Solution 3

Look into the Buffer class:

ushort[] theImageData = inImageData.DataArray;

byte[] buf = new byte[Buffer.ByteLength(theImageData)]; // 2 bytes per short
Buffer.BlockCopy(theImageData, 0, buf, 0, Buffer.ByteLength(theImageData));

string theOutData = System.Text.Encoding.ASCII.GetString(buf);

Solution 4

Just FYI, this has been fixed in later revision (gdcm 2.0.10). Look here:

http://gdcm.sourceforge.net/

-> http://apps.sourceforge.net/mediawiki/gdcm/index.php?title=GDCM_Release_2.0

Share:
11,030
mmr
Author by

mmr

Updated on June 30, 2022

Comments

  • mmr
    mmr almost 2 years

    I have a very painful library which, at the moment, is accepting a C# string as a way to get arrays of data; apparently, this makes marshalling for pinvokes easier.

    So how do I make a ushort array into a string by bytes? I've tried:

    int i;
    String theOutData = "";
    ushort[] theImageData = inImageData.DataArray;
     //this is as slow like molasses in January
     for (i = 0; i < theImageData.Length; i++) {
         byte[] theBytes = System.BitConverter.GetBytes(theImageData[i]);
         theOutData += String.Format("{0:d}{1:d}", theBytes[0], theBytes[1]);
     }
    

    I can do it this way, but it doesn't finish in anything remotely close to a sane amount of time.

    What should I do here? Go unsafe? Go through some kind of IntPtr intermediate?

    If it were a char* in C++, this would be significantly easier...

    edit: the function call is

    DataElement.SetByteValue(string inArray, VL Length);
    

    where VL is a 'Value Length', a DICOM type, and the function itself is generated as a wrapper to a C++ library by SWIG. It seems that the representation chosen is string, because that can cross managed/unmanaged boundaries relatively easily, but throughout the C++ code in the project (this is GDCM), the char* is simply used as a byte buffer. So, when you want to set your image buffer pointer, in C++ it's fairly simple, but in C#, I'm stuck with this weird problem.

    This is hackeration, and I know that probably the best thing is to make the SWIG library work right. I really don't know how to do that, and would rather a quick workaround on the C# side, if such exists.

  • mmr
    mmr over 15 years
    sorry, the ushorts are across the entire dynamic range, 0 to 65535.
  • mmr
    mmr over 15 years
    that got it! Thanks! (the first part appears to be fast enough, even with the extra copy).
  • Mel
    Mel over 15 years
    So I misunderstood the earlier comment about 8-bit chars? Sorry.