3

我有一个非常痛苦的库,目前它正在接受 C# 字符串作为获取数据数组的一种方式;显然,这使得编组 pinvokes 更容易。

那么如何将 ushort 数组按字节转换为字符串呢?我试过了:

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

我可以这样做,但它不会在任何接近理智的时间内完成。

我应该在这里做什么?不安全?通过某种 IntPtr 中间体?

如果它是 C++ 中的 char*,这会容易得多......

编辑:函数调用是

DataElement.SetByteValue(string inArray, VL Length);

其中 VL 是一个“值长度”,一个 DICOM 类型,并且函数本身是由 SWIG 作为 C++ 库的包装器生成的。似乎选择的表示是字符串,因为它可以相对容易地跨越托管/非托管边界,但是在整个项目中的 C++ 代码(这是 GDCM)中,char* 只是用作字节缓冲区。所以,当你想设置你的图像缓冲区指针时,在 C++ 中它相当简单,但在 C# 中,我遇到了这个奇怪的问题。

这是黑客攻击,我知道最好的办法可能是让 SWIG 库正常工作。我真的不知道该怎么做,如果存在的话,我宁愿在 C# 端快速解决。

4

6 回答 6

7

P/Invoke 实际上可以在大多数时间使用 StringBuilder 创建可写缓冲区来处理您所需要的内容,例如,请参阅pinvoke.net 上的 GetWindowText 和相关函数

然而,除此之外,数据为 ushort,我假设它是用 UTF-16LE 编码的。如果是这种情况,您可以使用 Encoding.Unicode.GetString(),但这将期望一个字节数组而不是一个 ushort 数组。要将您的 ushorts 转换为字节,您可以分配一个单独的字节数组并使用 Buffer.BlockCopy,如下所示:

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

但是,如果不安全代码是可以的,您还有另一个选择。以 ushort* 形式获取数组的开头,并将其硬转换为 char*,然后将其传递给字符串构造函数,如下所示:

string asString;
unsafe
{
    fixed (ushort *dataPtr = &data[0])
        asString = new string((char *) dataPtr, 0, data.Length);
}
于 2008-11-08T02:02:56.980 回答
1

您可以做的一件事是从使用字符串切换到 stringBuilder,它将极大地提高性能。

如果您愿意使用不安全的代码,您可以使用指针并像您的 c++ 一样实现您的 c# 代码。或者你可以编写一个小的 c++\cli dll 来实现这个功能。

于 2008-11-08T02:02:12.037 回答
1

查看Buffer类:

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);
于 2008-11-08T02:16:43.363 回答
1

仅供参考,这已在以后的修订版(gdcm 2.0.10)中得到修复。看这里:

http://gdcm.sourceforge.net/

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

于 2008-12-18T13:57:56.860 回答
0

我不太喜欢这个,但考虑到以下假设,它似乎有效:

1. 每个 ushort 都是 0 到 127 之间的 ASCII 字符

2. (好吧,我想只有一个假设)

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

我确信有更好的方法可以做到这一点,但这是我能很快想到的。

于 2008-11-08T02:14:29.390 回答
0

您可以通过这种方式避免不必要的复制:

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);
    }
}
于 2021-01-23T14:00:19.333 回答