ASCII/多字节到Unicode转换
一篇关于使用C#和SQL/CLR集成将ASCII/多字节转换为Unicode的文章。
引言
在转换旧的数据库应用程序时,一个常见的问题是客户有很多数据,他们不想丢失这些数据。数据字符串通常以ASCII/多字节编码,但我们的新系统使用NVARCHAR
字段,所以问题是我们必须将旧数据转换为Unicode。这正是我们在这里要做的。
我必须强调,我将要介绍的方法对于大多数情况来说是“超大”的:在常见情况下,您将处理单个代码页;在这种情况下,SQL Server内置工具就足够了。但是,在某些情况下需要更高级的方法,例如,当您有一个存储具有不同代码页的字符串的旧数据库时。
作为一个完整的例子,为了向您展示SQL-CLR集成的功能,我还决定使用Win32 API来执行转换,这样您也可以看到如何从SQL中使用P/Invoke。如果您有一个旧的DLL并且想使用它,这可能很有用……但是*请注意*,这可能真的非常危险……如果您不完全了解您在做什么,您可能会关闭整个SQL Server进程!!!
只是一个说明:我不提供测试项目,因为我将在这里展示的代码确实很简单,您可以复制和粘贴,而且更快!
解决方案
我想展示的解决方案既简单又强大。它由两个逻辑部分组成:首先,我们将转换例程构建为一个标准的C#函数。其次,我们将它集成到SQL Server中作为一个函数,这样您就可以将其与T-SQL一起使用。
显然,根据您的应用程序场景,这可能不是最好的方法,所以一旦您有了转换例程,您可以选择按照我的方法,或者您可能更喜欢在执行批量转换的外部应用程序中使用它,或者您可能会想到其他方法。
第 1 部分:创建转换例程
我使用了以下众所周知的 Win32 API
int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cbMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
这里是用于P/Invoking的C#签名
[DllImport("kernel32.dll")]
private static extern int MultiByteToWideChar(
uint CodePage,
uint dwFlags,
[MarshalAs(UnmanagedType.LPArray)] Byte[] lpMultiByteStr,
int cbMultiByte,
[Out, MarshalAs(UnmanagedType.LPArray)] Byte[] lpWideCharStr,
int cchWideChar);
现在编写一个执行转换的类非常容易
namespace ConvUtils {
public static class Unicode {
public static SqlString ConvToUnicode(SqlInt32 codepage , SqlString multibyteString) {
byte[] b = (byte[])iConvToMultibyteArray(multibyteString);
return (SqlString)ToUnicode((uint)(int)codepage, b);
}
private static string ToUnicode(uint codepage, Byte[] lpMultiByteStr) {
Byte[] lpWideCharStr = new Byte[2*lpMultiByteStr.Length];
MultiByteToWideChar(codepage, 0, lpMultiByteStr, lpMultiByteStr.Length,
lpWideCharStr, 2*lpMultiByteStr.Length);
return System.Text.Encoding.Unicode.GetString(lpWideCharStr);
}
private static SqlBinary iConvToMultibyteArray(SqlString multibyteString) {
byte[] result = multibyteString.GetUnicodeBytes();
return (SqlBinary)result;
}
}
}
这个例子非常简单,不需要任何其他解释。SQL类型已被使用,因为我将把它集成到SQL Server中,但如果您不需要它,您可以将它们替换为字符串和字节数组。
我还决定让代码页成为一个参数,因为您的PC上或SQL Server上的代码页可能与转换所需的不同。
第 2 部分:SQL Server 代码
好的,现在我们有一个将ASCII/多字节转换为Unicode的C#代码。下一步是将其集成到SQL Server中,以便任何数据库用户都可以访问此转换例程
首先,创建一个可以由SQL Server托管的DLL:我们所要做的就是将[SQLFunction]
属性添加到ConvToUnicode
[SQLFunction]
public static SqlString ConvToUnicode( ...
最后,构建DLL并使用类似以下的脚本将其集成到SQL Server中
use TESTDB
go
exec sp_configure "clr enabled", '1'
go
reconfigure
go
-- for test purpouses only, not recommended in production environments
ALTER DATABASE TESTDB SET TRUSTWORTHY ON
go
begin try
CREATE ASSEMBLY [asmUni] FROM 'c:\project_output_dir\uniconv.dll'
with permission_set=UNSAFE
end try
begin catch
alter assembly [asmUni] FROM 'c:\project_output_dir\uniconv.dll' WITH UNCHECKED DATA
end catch
go
if exists(
select name from sys.objects where name =
'csConvToUnicode') drop function [dbo].[csConvToUnicode]
go
CREATE FUNCTION [dbo].[csConvToUnicode] (
@codepage int,
@multibytestr nvarchar(max)
) returns nvarchar(max)
AS EXTERNAL name [asmUni].[ConvUtils.Unicode].[ConvToUnicode]
go
就是这样!
现在,您可以像使用任何其他函数一样使用此函数,例如,在经典的Select
语句中,创建视图或创建自动保持数据更新的触发器。
这是一个关于我们如何在T-SQL语句中使用此函数的最终示例(950是繁体中文的代码页)
select
description,
dbo.csConvToUnicode(950, description) as converted
from testtable
description converted
----------------------- -------------------
¨à µ£ºô¸ô¦w¥þ 兒童網路安全
°ê»Ú¸ê°T °T°T°T°T°T 國際資訊 訊訊訊訊訊
°ê»Ú¸ê°T °T°T°T°T°T 國際資訊 訊訊訊訊訊
a a
Ãô¨pÃ…v¬Fµ¦ éš±ç§æ¬Šæ”¿ç–
test c test c
结论
我展示了一种非常简单但强大的方法,可以利用SQL Server提供的CLR集成将旧的ASCII/多字节数据转换为Unicode。我希望这对于您的个人解决方案来说是一个很好的起点。
历史
- 2007年2月16日 - 在介绍中添加了更多评论。
- 2007年2月8日 - 第一个版本。