跳到主要內容

 none
64位元 Unicode 的字串長度如何正確取得! RRS feed

  • 問題

  • 最近用C++寫一個DLL, 我對C++不太熟! 尤其是"字串"!!!!!

    我被字串搞了好幾天幾夜了! 在C#之中, 只有一個string搞定一切, 不管幾位元, 不管Unicode編碼....

    首先, 我的設是在64位元, Unicode編碼的情況之下!

    我要將一個lpstr字串, 變更為lpwcstr.....

    我利用了MultiByteToWideChar 這個函數如下! 這是網路上的分享!

    LPCWSTR ustrT(LPSTR src) {
    int sBufSize = strlen(src);
    DWORD dBufSize = MultiByteToWideChar(CP_ACP, 0, src, sBufSize, NULL, 0);
    wchar_t *dBuf = new wchar_t[dBufSize];
    wmemset(dBuf, 0, dBufSize);
    int nRet = MultiByteToWideChar(CP_ACP, 0, src, sBufSize, dBuf, dBufSize);
    return dBuf;
    }

    這個函式回傳的結果, 只會有第一個字! 原因出在dBufSize = MultiByteToWideChar(CP_ACP, 0, src, sBufSize, NULL, 0); 取得需要的記憶體空間有錯!

    我必需將 dBufSize= dBufSize * 2, 這樣才可以得到一個正確的結果!

    我不知道為何需要這樣? 煩請各位先進指教!

    dBufSize * 2, 這樣才可以得到一個正確的結果!


    我不知道為何需要這樣? 

    另外, 我直接LPCWSTR strLpcwstr =  (LPCWSTR)strLpstr 這樣的轉換, 也得不到我想要的結果! 為什麼?

    煩請各位先進指教!


    2019年9月1日 上午 02:40

解答

  • 字串没有64位和32位的分别,LPSTR是单字节的utf8或ansi编码,LPWSTR是双字节的unicode(也称作utf16)编码,这是编码上的区别,这个和c++无关。c#在代码中不支持utf8和ansi,c#中的字串全部为utf16,因此没有此问题。c和c++要支持很广阔的应用范围,比如内存有限的设备(工控机、军用设备、航天设备等等),因此它的字串有宽(utf16)和窄(utf8)之分。

    LPCSTR和LPCWSTR是const字串,c#中同样没有这个区分,c#中所有字串全部为const,c#不支持非const字串,因此c#字串相当于LPCWSTR。

    strlen返回的是字串中的字符数,因为不包含结尾符,所以得到的数值必须+1。

    正确用法如下:

    LPWSTR ustrT(LPCSTR src)
    {
        auto const bufSize = MultiByteToWideChar(CP_ACP, 0, src, -1, nullptr, 0);
        auto const buf = new wchar_t[bufSize];
        MultiByteToWideChar(CP_ACP, 0, src, -1, buf, bufSize);
        return buf;
    }


    LPWSTR ustrT(LPCSTR src)
    {
        auto const srcSize = strlen(src) + 1u;
        auto const bufSize = MultiByteToWideChar(CP_ACP, 0, src, srcSize, nullptr, 0);
        auto const buf = new wchar_t[bufSize];
        MultiByteToWideChar(CP_ACP, 0, src, srcSize, buf, bufSize);
        return buf;
    }
    • 已標示為解答 GaryChiang 2019年9月1日 上午 07:14
    2019年9月1日 上午 03:33

所有回覆

  • 字串没有64位和32位的分别,LPSTR是单字节的utf8或ansi编码,LPWSTR是双字节的unicode(也称作utf16)编码,这是编码上的区别,这个和c++无关。c#在代码中不支持utf8和ansi,c#中的字串全部为utf16,因此没有此问题。c和c++要支持很广阔的应用范围,比如内存有限的设备(工控机、军用设备、航天设备等等),因此它的字串有宽(utf16)和窄(utf8)之分。

    LPCSTR和LPCWSTR是const字串,c#中同样没有这个区分,c#中所有字串全部为const,c#不支持非const字串,因此c#字串相当于LPCWSTR。

    strlen返回的是字串中的字符数,因为不包含结尾符,所以得到的数值必须+1。

    正确用法如下:

    LPWSTR ustrT(LPCSTR src)
    {
        auto const bufSize = MultiByteToWideChar(CP_ACP, 0, src, -1, nullptr, 0);
        auto const buf = new wchar_t[bufSize];
        MultiByteToWideChar(CP_ACP, 0, src, -1, buf, bufSize);
        return buf;
    }


    LPWSTR ustrT(LPCSTR src)
    {
        auto const srcSize = strlen(src) + 1u;
        auto const bufSize = MultiByteToWideChar(CP_ACP, 0, src, srcSize, nullptr, 0);
        auto const buf = new wchar_t[bufSize];
        MultiByteToWideChar(CP_ACP, 0, src, srcSize, buf, bufSize);
        return buf;
    }
    • 已標示為解答 GaryChiang 2019年9月1日 上午 07:14
    2019年9月1日 上午 03:33
  • https://tlcheng.wordpress.com/2009/11/08/%e6%b8%ac%e8%a9%a6%e8%a8%98%e9%8c%84-utf32-%e4%bd%8d%e5%85%83%e7%b5%84%e6%95%b8%e8%88%87-char-%e5%9e%8b%e5%88%a5/

    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2019年9月1日 上午 04:41
  • 再請教前輩! 

    我在C++ DLL內宣告了一個

    typedef struct
    {
    int orderID;
    int date;
    int time;
    int contracts;
    double price;
    char future[20];
    }OrderMsg;

    然後利用sendmessage傳給了一個C#的form

    在C#內我宣告

            [StructLayout(LayoutKind.Sequential)]
            public struct Order
            {
                public int orderID;
                public int date;
                public int time;
                public int contracts;
                public double price;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string future;
            }

    目前可以正常收到字串! 但是不是又是個"巧合"而已! 如前所述, char[] 是utf8, 而string是utf16!

    謝謝你!


    2019年9月1日 下午 01:09
  • 标记了UnmanagedType.ByValTStr后.Net会自动帮你转换,这个不是巧合。不过在windows操作系统中不支持utf8,只支持ansi,如果是utf8那可能需要先在c++中转换好。

    https://docs.microsoft.com/zh-tw/dotnet/api/system.runtime.interopservices.unmanagedtype?view=netframework-4.8

    https://docs.microsoft.com/zh-tw/dotnet/standard/native-interop/charset?view=netframework-4.8

    2019年9月2日 上午 12:46
  • unicode 目前有3種, utf-8, utf-16, utf-32

    如果你要處理的字串, 本來就是 utf-16, 那直接用 wcslen 就可以取得字串長度了.

    WCHAR *wz =  L"中文測試,你好嗎?"; //
    int n = wcslen(wz); // n 即字串長度

    喜好 class 的話, 可用 wstring

    wstring ws = L"中文測試,你好嗎?";
    int n = ws.length(); // n 同上


    如果你是要把 big5, utf8, gb.. 等等, 轉成 utf-16, 那就要 MultiByteToWideChar 來轉碼了.

    因為 MultiByteToWideChar 本身就可以得到轉換後的字串長度, 所以就順便拿來用, 不必事後再去取一次字數.

    > 我必需將 dBufSize= dBufSize * 2, 這樣才可以得到一個正確的結果!
    > 我不知道為何需要這樣? 煩請各位先進指教!
    > dBufSize * 2, 這樣才可以得到一個正確的結果!

    你要的是 byte數 嗎?

    字串長度 一般是指 字數.. 因為 utf-16 每個字 佔 2個byte, 要所以要 * 2.

    new WCHAR[1] 這是開 1 個 WCHAR 的空間, 因為 WCHAR 是 2個byte, 所以它會開 2個byte 空間.

    所以用 malloc 時, 才要 malloc( 1 * sizeof(WCHAR) ),  多乘 sizeof(WCHAR) 單位byte數..

    轉換時, 最好至少 多一個字的空間來當 gap, 因為要用來補零..

    因為 轉出字數 和 伸請的空間 剛剛好一樣, 那字串後面會沒辦法補 0, 很可能出現, 超出記憶體 的 錯誤..

    如果輸出是在 wp[] , n 是輸出字數

    最後再加一行 wp[n] = 0; 就好了, 只補一個零, 會比去 清除 整塊buffer 快的多.

    • 已編輯 jms1st 2019年10月9日 上午 09:50
    2019年10月9日 上午 09:16