【问题标题】:Handling of Unicode Characters using Delphi 6使用 Delphi 6 处理 Unicode 字符
【发布时间】:2013-01-10 03:42:59
【问题描述】:

我有一个用 Delphi 6 开发的轮询应用程序。 它读取文件,根据规范解析文件,执行验证并上传到数据库(SQL Server 2008 Express Edition

我们必须为具有双字节字符集 (DBCS) 的操作系统提供支持,例如日本操作系统。 因此,我们将 SQL Server 中的数据库字段从 varchar 更改为 nvarchar。

轮询在带有 DBCS 的操作系统中运行良好。它也适用于非 DBCS 操作系统,如果 系统区域设置为日文/中文/韩文,操作系统具有相应的语言包。 但是,如果 Locale 设置为英语,则数据库包含双字节字符的垃圾字符。

我进行了一些测试,但未能确定解决方案。

例如如果我使用 TStringList 从 UTF-8 文件中读取并将其保存到另一个文件,则保存 Unicode 数据。 但是,如果我使用文件的内容使用 TADOQuery 组件运行更新查询,则会显示垃圾字符。 该数据库还包含垃圾字符。

PFB示例代码:

var
    stlTemp : TStringList;
    qry : TADOQuery;
    stQuery : string;
begin
    stlTemp := TStringList.Create;
    qry := TADOQuery.Create(nil);
    stlTemp.LoadFromFile('D:\DelphiUnicode\unicode.txt');
    //stlTemp.SaveToFile('D:\DelphiUnicode\1.txt'); // This works. Even though 
    //the stlTemp.Strings[0] contains junk characters if seen in watch

    stQuery := 'UPDATE dbo.receivers SET company = ' + QuotedStr(stlTemp.Strings[0]) +
        ' WHERE receiver_cd = N' + QuotedStr('Receiver'); 
    //company is a nvarchar field in the  database
    qry.Connection := ADOConnection1;
    with qry do
    begin
        Close;
        SQL.Clear;
        SQL.Add(stQuery);
        ExecSQL;
    end;
    qry.Free;
    stlTemp.Free
end;

以上代码在 DBCS 操作系统中运行良好。

我尝试过使用字符串、宽字符串和 UTF8String。但是,如果语言环境设置为英语,这在英语操作系统中不起作用。

请提供有关此问题的任何指示。

【问题讨论】:

  • “D:\DelphiUnicode\unicode.txt”是什么意思?有很多 unicode 格式,例如 UTF-8、UTF-7 和少数 UTF-16 的变体尝试使用 TWideStringList 加载 UTF-16 文件。如果你的 Delphi 没有它 - 试试 Jedi CodeLibrary 中的 WideStringList
  • 由于 Delphi 6 是相当旧的版本(除非应用了所有更新,否则存在相当多的错误),您是否考虑过迁移到 Delphi 或 Lazarus/CodeTyphoon 的一些现代版本?
  • 其实你的代码很脆弱。阅读 SQL 注入并使用参数化查询。 bobby-tables.com 那也许还可以让你为参数指定 WideString 类型
  • @ArnaudBouchez RDBMS 被报告为一些“SQL Server”,可能是 Microsoft SQL Server 的某些版本
  • @Arioch'The :文件是 UTF-8。数据库是 SQL server express。这是测试应用程序,因此没有放置参数化查询。另外,我错过了一件事,如果我将 varchar 数据类型修改为 nvarchar,那么数据库插入就不是问题了。

标签: delphi unicode ado delphi-6 tadoquery


【解决方案1】:

在非 Unicode Delphi 版本中,基本情况是您需要使用 WideStrings (Unicode) 而不是 Strings (Ansi)。

忘记TADOQuery.SQL (TStrings),使用TADODataSet.CommandTextTADOCommand.CommandText(WideString) 或将TADOQuery 类型转换为TADODataSet。例如:

stlTemp: TWideStringList; // <- Unicode strings - TNT or other Unicode lib
qry: TADOQuery;
stQuery: WideString; // <- Unicode string

TADODataSet(qry).CommandText := stQuery;
RowsAffected := qry.ExecSQL;

您也可以使用TADOConnection.Execute(stQuery) 直接执行查询。


对参数化查询要格外小心:ADODB.TParameters.ParseSQL 是 Ansi。如果ParamCheck 为真(默认)TADOCommand.SetCommandText->AssignCommandText 将导致 如果您的查询是 Unicode(InitParameters 是 Ansi),则会出现问题。

(请注意,您可以直接使用 ADO Command.Parameters - 使用 ? 字符作为参数的占位符,而不是 Delphi 的约定 :param_name)。


QuotedStr 返回 Ansi 字符串。您需要此函数的 Wide 版本 (TNT)


另外,正如@Arioch '提到的TNT Unicode Controls 套件是您制作Delphi Unicode 应用程序的最佳选择。 它具有在应用程序中成功管理 Unicode 任务所需的所有控件和类。

简而言之,你需要考虑宽广 :)

【讨论】:

  • TWideStringList - 它在 D6 的后面吗?
  • @Arioch'The,没有。 TWideStringList 需要 TNT 或其他 Unicode 库。
  • 它是否能够加载 UTF-8 文件,或者他必须通过内存流传递脚本文件并自己将其重新编码为 UTF-16。虽然并非不可能,但它似乎超出了当前的 topicstarter 功能。说到 JCL,我很惊讶 TJclWideStrings 不支持 UTF-8 加载,但 TWideStringList 可以。也许有一天有人会合并它们......
  • @kobik:感谢您的意见。我会尝试这些建议。
【解决方案2】:
  1. 您没有指定数据库服务器,因此这项调查仍由我们负责。您应该检查您的数据库服务器如何支持 Unicode。这意味着如何为数据库和其中的表/列/索引/排序规则/等指定 Unicode 字符集。您必须确保整个数据库的每个细节都普遍支持 Unicode,以避免数据丢失。

  2. 通常您还应该检查您的数据库连接(使用选择的数据库访问库)是否也启用了 unicode。通常,Microsoft ADO 与 OLE 一样,都应该启用 Unicode。但仍然检查您的数据库服务器手册如何在连接字符串中指定 unicode 代码页或字符集。非 Unicode 连接也可能导致数据丢失。

  3. 当您告诉您阅读了一些 unicode 文件时 - 它是模棱两可的。什么 ius unicode 文件?是 UTF-8 吗?还是 UTF-16 的四种风格之一?还是 UTF-7 ?还是其他一些 Unicode 传输格式?通常的 windows WideChar 大致对应于旧版 UCS-2,并且预计是 UTF-16 的 BOM 剥离 Intel-Endian 风格。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms221069.aspx

  4. 如果文件肯定是 UTF-16 的风格,那么您可以使用 Delphi TWideStringList 或 Jedi CodeLibrary TJclWideStringList 加载它。查看您从未使用字符串变量处理数据的代码 - 在任何地方使用 WideString 以避免数据丢失。
    由于 D6 是 bug 最多的版本之一,我宁愿确保安装 Delphi 的每个更新,然后安装和使用 JCL。 JCL 还提供代码页转换功能,这可能比普通的AnsiStringVar := WideStringVar 方法更灵活。
    对于 UTF-8 文件,可以通过 JCL 的 TWideStringList 类加载(但不是 TJclWideStringList)。

  5. 调试时,将列表的行加载到WideString变量中,查看其内容是否保留。

  6. 不要这样写查询。请参阅http://bobby-tables.com/ 即使您没有预料到恶意破解者 - 您也可以自己犯错误或获取意外数据。随时随地使用参数化查询!永远!
    请参阅此类示例:http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/ADODB_TADOQuery_Parameters.html
    检查每个SQL VARCHAR 参数是否为ftWideString 以包含Unicode,而不是ftString。检查字段(列)是否相同。

  7. 想想是否可以抛弃遗留技术,因为它们的支持只会随着时间的推移而变得更加困难。

    7.1。由于 Microsoft ADO 已弃用(例如较新版本的 Microsoft SQL Server 不支持它),请考虑切换到“实时”数据访问库。像 AnyDAC、UniDAC、ZeosDB 或其他一些库。 Torry.net 可能会给你一些提示。

    7.2。由于 Delphi 6 RTL 和 VCL 不支持 Unicode,如果您设法找到它们的免费版本或购买它们,请考虑将您的应用程序迁移到 TNT Unicode 组件。或者迁移到较新的 Delphi 版本。

    7.3。由于 Delphi 6 非常陈旧且长期不受支持,而且它是最有漏洞的 Delphi 版本之一,因此请考虑迁移到较新的 Delphi 版本或 CodeTyphoon 或 Lazarus 等免费工具。作为奖励,Lazarus 在其最近的 beta 版本中开始迁移到 Unicode,并且在迁移结束时,您可能会让您的应用程序准备好 unicode。

    7.4 迁移可能是重构应用程序和摆脱遗留意大利面条的借口和刺激因素。

【讨论】:

  • @whosrdaddy,使用 ADO 的非 Unicode Delphi 中的参数化查询有一个很大的问题,因为它们无法正确处理 Unicode。看我的回答。
  • @Kobik:这个事实已经是升级到支持 unicode 的 delphi 版本的一个很好的理由 :)
  • @whosrdaddy,如果你有钱和时间来升级,那是逃离这个痛苦世界的最佳方式:)
  • @whosrdaddy 或将 ADO 更改为更活跃的数据库访问库
  • @Arioch'The,问题不是 ADO,而是它的非 Unicode Delphi 实现。如果实现使用 Ansi Strings 而不是 WideStrings/BSTR(按照 ADO 的意图),是死是活,没关系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 2018-12-05
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 2011-02-22
相关资源
最近更新 更多