简体   繁体   中英

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

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.

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);
}

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.

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

I don't like this much, but it seems to work given the following assumptions:

1. Each ushort is an ASCII char between 0 and 127

2. (Ok, I guess there is just one assumption)

        ushort[] data = inData; // The ushort array source

        Byte[] bytes = new Byte[data.Length];  // Assumption - only need one byte per ushort

        int i = 0;
        foreach(ushort x in data) {
            byte[] tmp = System.BitConverter.GetBytes(x);
            bytes[i++] = tmp[0];
            // Note: not using tmp[1] as all characters in 0 < x < 127 use one byte.
        }

        String str = Encoding.ASCII.GetString(bytes);

I'm sure there are better ways to do this, but it's all I could come up with quickly.

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);

You can avoid unnecessary copying this way :

public static class Helpers
{
    public static string ConvertToString(this ushort[] uSpan)
    {
        byte[] bytes = new byte[sizeof(ushort) * uSpan.Length];

        for (int i = 0; i < uSpan.Length; i++)
        {
            Unsafe.As<byte, ushort>(ref bytes[i * 2]) = uSpan[i];
        }

        return Encoding.Unicode.GetString(bytes);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM