【转自CSDN】深入 Microsoft.VisualBasic.Strings.StrConv 簡繁轉換
深入 Microsoft.VisualBasic.Strings.StrConv 簡繁轉換
昨天又遇到一個簡繁轉換的需求, 雖然這個問題以前已經處理過了, 但是以前是用自己建立的 b52gb 和 gb2b5 的對應表來完成這個需求(VB6 的話就用 StrConv 方法來達成), 在 .NET 環境中, Microsoft.VisualBasic.dll 裡也有提供 Strings.StrConv 方法, 而且用法和原來的 VB6 幾乎是如出一轍, 可是昨天在使用 StrConv 的時候卻意外發現了一些奇怪的現象, 特別深入研究了一下, 順便記錄下來!
先來觀察 Strings.StrConv 方法的簽名:
public static string StrConv(string str, VbStrConv Conversion, [Optional, DefaultParameterValue(0)] int LocaleID)
第三個參數和 MSDN 上的文件有點不同, 上面的簽名是從 Reflector 中摘出來的, 也是這篇文章要記錄的重點, 先來看一些範例:
a1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0404); // a1 = "?樂??"
a2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0404); // a2 = "????" b1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0804); // b1 = "書樂う?"
b2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0804); // b2 = "书乐う?" c1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0412); // c1 = "?樂う반"
c2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0412); // c2 = "??う반" d1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0009); // d1 = "書樂う반"
d2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0009); // d2 = "书乐う반"
上面 8 個範例的第一個參數摻雜了簡中、繁中、日文和韓文, 第二個參數區分了轉簡體和轉繁體, 第三個參數是 localeID 的部分, 分別包含了 zh-TW (0x0404), zh-CN (0x0840), ko-KR (0x0412), en (0x0009), 讓我們來仔細觀察一下結果, 一切的玄機都在第三個 localeID 參數身上. 我們先假設第三個參數 localeID 是用來表示來源字串的字集, 所以如果這個假設成立的話..., 來看看結果:
- a1: 嗯, 一切如預期的結果, 第一步應該先將 "书樂う반" 轉成符合 zh-TW (0x0404) 的字集, 所以結果是 "?樂??", 然後再根據第二個參數 VbStrConv.TraditionalChinese 結果變成了 "?樂??", 正確!
- a2: 第一步同上, 然後再根據第二個參數 VbStrConv.SimplifiedChinese 結果應該要變成 "?乐??", 可是 a2 的結果卻得到了 "????", 不如預期!
- b1: 第一步應該先將 "书樂う반" 轉成符合 zh-CN 的字集, 所以結果是 "书樂う?", (簡體字集是有包含繁體形態 "樂" 這個字的), 第二個參數 VbStrConv.TraditionalChinese, 所以結果變成 "書樂う?", 正確!
- b2: 正確!
- c1: 韓文字集不太了解, 從結果推測韓文的漢字集如果沒有 "书" 這個字的話, 結果應該算是正確的!
- c2: 從 c1 的結果本來預期應該得到 "?乐う반", 可是結果卻是 "??う반", 不如預期!
- d1: 咦!!! 怎麼會這樣, 完全不如預期, 竟然得到如此漂亮的結果, 本來預期是 4 個 "?" 的!!!
- d2: 一樣得到令人搞不清楚為什麼美麗結果!!!
這到底是怎麼一回事? 是假設錯誤嗎? 可是還有什麼別的可能嗎? 為了解開這個謎團, 於是又祭出了殺手工具 "Reflector", 仔細觀察了 Microsoft.VisualBasic.dll 內的程式碼, 終於了解箇中奧秘!
先來看一下 StrConv 方法反向工程之後的一小部分程式碼(還沒到重點, 所以只節錄最後幾行),
再來追進 vbLCMapString 看一看, 也是看下半部就行了:
橘黃色是和 Encoding 相關的程式碼, 綠色和紅色底線的部分是 Win32 API 用來處理字碼轉換的函式, 綠色底線的函式有一個後綴字 A, 而且輸入的參數是 byte[], 而紅色底線部分的函式則沒有後綴字, 輸入的參數是 string.
看到這兒, 答案已經呼之欲出了, 之所以結果會不如預期都是因為 encoding.GetBytes() 和 encoding.GetString() 這兩個方法給弄砸的, 如果可以跳過它們直接叫用底下畫紅線的 UnsafeNativeMethods.LCMapString 的話, 就不會有那些討厭的問號產生了, 那要怎麼樣才能避過那段我們不想要的程式碼呢? 看一下那個底下有畫虛線的部分 "encoding.IsSingleByte", 嗯! 沒錯, 這就是為什麼 d1, d2 的結果這麼令人驚訝的原因了, 因為 en 的 Encoding 就是 SingleByte 所以會直接跳過 Unicode 和 MBCS 互轉的部分, 而直接進行 Unicode 的轉碼, 於是得到美麗的答案, 整個過程分析完畢!
雖然已經知道整個來龍去脈, 但是如果能再了解一下那個神奇的 Win32API: LCMapString 的話, 想必觀念又可以再更清楚一些. 所以我們再來看看 LCMapString 的重點吧! 嗯~~重點在哪兒咧? 以此篇文章的需求 "簡繁轉換" 來看的話, 只有第二個參數 dwMapFlags 值得我們注意, 打開 MSDN 的文件, 透過索引找到 LCMapString 的章節, 我們可以看到以下的內容,
針對 Windows NT 4.0 以後的作業系統, Microsoft 已經早就幫程式設計師們準備好了一個現成的系統函式來達成簡繁轉換的工作了(唉! 為什麼沒有早點知道!), 看你是要轉簡體 (LCMAP_SIMPLIFIED_CHINESE), 或是轉繁體 (LCMAP_TRADITIONAL_CHINESE), 只要給個參數, 一切就搞定了, 就是這麼簡單!
結論
如果您的需求和我一樣, 只是想把文字內容的簡繁部分轉換, 並不是想轉成 big5 或 gb, 整個輸出入都是 unicode, 而且也不想破壞其他非簡繁文字部分的話, 那麼結論就是照著本篇文章的一開始的 d1, d2 範例呼叫 VB 的 Strings.StrConv 帶上 0x0009 或是其他 SingleByte 字集的 localeID 當成第三個參數就可以啦!!!
如果不想引入 Microsoft.VisualBasic.dll (別問為什麼, 純屬個人偏好) 又想要做到相同的效果, 做法也很簡單, 請參考以下的範例程式碼!!!
public static class ChineseStringUtility
{
internal const int LOCALE_SYSTEM_DEFAULT = 0x0800;
internal const int LCMAP_SIMPLIFIED_CHINESE = 0x02000000;
internal const int LCMAP_TRADITIONAL_CHINESE = 0x04000000; [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int LCMapString(int Locale, int dwMapFlags, string lpSrcStr, int cchSrc, [Out] string lpDestStr, int cchDest); public static string ToSimplified(string source)
{
String target = new String(' ', source.Length);
int ret = LCMapString(LOCALE_SYSTEM_DEFAULT, LCMAP_SIMPLIFIED_CHINESE, source, source.Length, target, source.Length);
return target;
} public static string ToTraditional(string source)
{
String target = new String(' ', source.Length);
int ret = LCMapString(LOCALE_SYSTEM_DEFAULT, LCMAP_TRADITIONAL_CHINESE, source, source.Length, target, source.Length);
return target;
}
}
【转自CSDN】深入 Microsoft.VisualBasic.Strings.StrConv 簡繁轉換的更多相关文章
- Microsoft.VisualBasic.dll的妙用and 改善C#公共程序类库质量的10种方法
Microsoft.VisualBasic.dll的妙用(开发中肯定会用到哦) 前言 做过VB开发的都知道,有一些VB里面的好的函数在.NET里面都没有,而Microsoft.VisualBasic. ...
- Microsoft.VisualBasic.dll的妙用(开发中肯定会用到哦)
前言 做过VB开发的都知道,有一些VB里面的好的函数在.NET里面都没有,而Microsoft.VisualBasic.dll却给我们提供使用这些函数的功能(没用过VB的这些功能一样可以使用,大同小异 ...
- Microsoft.VisualBasic.DateAndTime.Timer 与 DateTime.Now.TimeOfDay.TotalSeconds 相当
如题,示例如下: Console.WriteLine(DateTime.Now.TimeOfDay.TotalSeconds); Console.WriteLine(Microsoft.VisualB ...
- 未能加载文件或程序集 Microsoft.VisualBasic.PowerPacks.Vs, Version=10.0.0.0 解决 亲测
项目打开winform程序做的某些窗体时报错: ************* 异常文本 ************** System.Reflection.TargetInvocationExceptio ...
- 一个实用的却被忽略的命名空间:Microsoft.VisualBasic
当你看到这个命名空间的时候,别因为是VB的东西就匆忙关掉网页,那将会是您的损失,此命名空间中的资源最初目的是为了简化VB.NET开发而创建的,所以Microsoft.VisualBasic并不属于Sy ...
- "一个实用的却被忽略的命名空间:Microsoft.VisualBasic":
当你看到这个命名空间的时候,别因为是vb的东西就匆忙关掉网页,那将会是您的损失,此命名空间中的资源最初目的是为了简化vb.net开发而创建的,所以microsoft.visualbasic并不 ...
- 利用Microsoft.VisualBasic中TextFieldParser解析器把CSV格式倒入数据库
阅读目录 利用ODBC去操作 利用TextFieldParser操作 写了个Demo,利用Microsoft.VisualBasic这个程序集中的TextFieldParser解析器解析CSV格式的文 ...
- Microsoft.VisualBasic.dll内置的判断变量类型的一系列实用方法
今天意外读到一线码农的一篇文章<挖一挖C#中那些我们不常用的东西之系列(2)--IsXXX 系列方法>,文章中讲到 Microsoft.VisualBasic.dll 里面的Informa ...
- WPF使用Microsoft.VisualBasic创建单例模式引起的权限降低问题
在进行WPF开发时,总是在找更加优雅去写单例模式的代码. 很多人都喜欢用Mutex,一个App.cs下很多的Mutex,我也喜欢用. 看完<WPF编程宝典>的第七章Applicaton类后 ...
随机推荐
- 模拟(堆):USACO Jan11 瓶颈
题目描述 Farmer John is gathering the cows. His farm contains a network of N (1 <= N <= 100,000) f ...
- strncpy 用法
strncpy 用法 原型:extern char *strncpy(char *dest, char *src, int n); 用法:#include <string.h> 功能: ...
- Unity3d shader之SWAP Force Depth-of-Field Shader
由于博主常年逃课,所以期末考试期间只能突击,但偶尔还能拿个奖学金啥的,哈哈,所以近一个月没有做游戏,也没有发博客= =... 这个景深的方法很简单 我们需要求的是CoC(circle of confu ...
- [GRYZ2014]迷宫问题
设有一个N*N方格的迷宫,入口和出口分别在左上角和右上角,迷宫格子中分别放有0和1,0表示可走,1表示不能走,迷宫走的规则如图.当迷宫给出之后,找出一条从入口到出口的通路. 输入:N N*N的迷宫 输 ...
- lfs遇到的一些问题--编制LFS
1.chroot后不要再打开新的终端了,没法用,还可能使系统崩溃.另外如果需要去睡觉,重启后要再次挂载并填充/dev和挂载虚拟内核文件系统,并再次运行chroot,可以将下列命令保存为脚本,重启后一次 ...
- basic mongodb
basic mongodb */--> pre { background-color: #2f4f4f;line-height: 1.6; FONT: 10.5pt Consola," ...
- 5个数求最值—南阳acm
问题描述 设计一个从5个整数中取最小数和最大数的程序 输入 输入只有一组测试数据,为五个不大于1万的正整数 输出 输出两个数,第一个为这五个数中的最小值,第二个为这五个数中 ...
- Java的垃圾回收概述
Java语言建立了垃圾收集机制,即GC,用以跟踪正在使用的对象和发现并回收不再使用的对象,垃圾清理势在必行,以下讲述java垃圾收集算法. 1.Java垃圾收集算法的核心思想 Java语言建立了垃圾收 ...
- 一次mysql瘫痪解救
最近手机app项目访问流量逐步的增加,对服务端webapi考验极大,是在一次新的业务消息推送后,极光推送给手机接受到的客户端达到19万个,此时app立马开始访问速度变慢了,用户体验相当差 客服接到的问 ...
- ctrl+z的JAVA实现,借助了命令模式(command pattern)
前些天学习<<JAVA与模式>>,到命令模式时,随带给了一个CTRL+Z案例的实现,想来学习编程这么久,CTRL+Z还没有认真实现过. 因此,借助JAVA与模式里面的源代码,自 ...