【问题标题】:Direct Oracle Access cuts off the end of unicode (wide) stringsDirect Oracle Access 切断 unicode(宽)字符串的结尾
【发布时间】:2013-08-30 19:56:39
【问题描述】:

我遇到了一个问题,Delphi XE2 中的 unicode(宽)字符串字段没有返回字符串的最后一部分,并且任何数据库控制组件也没有完全显示整个字符串到最后。

你可以看到下面的简单测试。

with TOracleDataSet.Create(self) do
try
  Session := OraSession;
  SQL.Text := 'CREATE TABLE test1 (fsString10 VARCHAR2(10))';
  ExecSQL;
  SQL.Text := 'INSERT INTO test1 (fsString10) VALUES ('''1234567890''')';
  ExecSQL;
  SQL.Text := 'INSERT INTO test1 (fsString10) VALUES ('''й234567890''')';
  ExecSQL;
  SQL.Text := 'INSERT INTO test1 (fsString10) VALUES ('''йцукенгшщз''')';
  ExecSQL;
  SQL.Text := 'SELECT fsString10 FROM test1';
  Open;
  while not Eof do
    ShowMessage(FieldByName('fsString10').AsString); 
    // '1234567890' turned into '1234567890' 
    // 'й234567890' turned into 'й23456789'
    // 'йцукенгшщз' turned into 'йцуке'
  SQL.Text := 'DROP TABLE test1';
  ExecSQL;
finally
  Free;
end;

如您所见,未正确加载 unicode 字符串。

另一方面,“Direct Oracle Access 4.1.3”组件在半个后记录后没有在字符串中保存字符。无论如何,它只保存前半串。

有办法解决吗?

  • BytesPerCharacter 设置为 bcAutoDetect。
  • 我尝试切换 NoUnicodeSupport 和所有其他选项,但没有成功。

有人对如何解决这个问题有任何想法吗?

PS:由于多种原因,我无法更改生产数据库架构。

DB Server NLS_DATABASE_PARAMETERS NLS_CHARACTERSET = CL8MSWIN1251
DB Client NLS_LANG=AMERICAN_AMERICA.UTF8
DB Client NLS_LANG=RUSSIAN_CIS.CL8MSWIN1251 <- the same thing

【问题讨论】:

  • 在你的调试中,你确定这是插入问题还是选择问题?
  • 您的数据库中配置了哪个字符集?您是否尝试过使用 NVARCHAR2 数据类型的示例?
  • @Rob,问题实际上是字符串缓冲区大小。
  • @TLama, NLS_CHARACTERSET = CL8MSWIN1251 但客户端是 unicode。数据库字段很难更改。

标签: string oracle delphi unicode delphi-xe2


【解决方案1】:

您的数据库字符集是什么(来自v$nls_parameters)?您是否在创建表之前为数据库或会话中设置了非默认 NLS_LENGTH_SEMANTICS

假设您的数据库字符集支持 Unicode(即 AL32UTF8)并且您使用默认的 NLS_LENGTH_SEMANTICSVARCHAR2(10) 最多分配 10 个字节的存储空间。由于 AL32UTF8 是一个可变宽度字符集,一个字符需要 1 到 3 个字节的存储空间。如果您以字节为单位声明列,这意味着您的列将能够存储 3 到 10 个字符,具体取决于您存储的特定字符。这似乎与您看到的行为一致。

最好的方法通常是使用字符长度语义来声明您的列。如果你声明你的表

CREATE TABLE test1 (fsString10 VARCHAR2(10 CHAR))

无论需要多少字节,您最多允许 10 个字符。这似乎是您想要的行为。

您可以在发出 DDL 之前通过设置 NLS_LENGTH_SEMANTICS 来更改会话级别的默认长度语义。如果你跑

ALTER SESSION SET nls_length_semantics = CHAR;

CREATE TABLE test1 (fsString10 VARCHAR2(10));

您的专栏还将使用字符长度语义(为最多 10 个字符而不是最多 10 个字节分配空间)。

也可以在系统级别设置nls_length_semantics。但是,Oracle 全球化人员通常不鼓励这样做,因为各种脚本(Oracle 和第三方)要么未在该配置中进行测试,要么已知存在问题。

如果您不想使用字符长度语义,您还可以将列的大小增加三倍(以字节为单位)。这将允许您的应用程序存储所有三个字符串。但这意味着如果它仅由英文字符组成,您可以存储 30 个字符的字符串。

【讨论】:

  • 感谢您的解决方案。你是对的,nls_length_semantics 是相等的字节。但由于多种原因,我无法更改数据库架构中的所有字段,对我来说这不是更好的解决方案。
  • 我已经根据您的方法使用更改的创建表模式运行了测试,但结果与上面显示的相同。
【解决方案2】:

我找到了另一种没有任何源代码更改的解决方案。

只需更改 oracle 客户端的 NLS_LANG 参数

AMERICAN_AMERICA.UTF8 

AMERICAN_AMERICA.CL8MSWIN1251 

并在主窗体创建事件上明确设置参数:

Windows.SetEnvironmentVariable(PChar('NLS_LANG'),
                               PChar('AMERICAN_AMERICA.CL8MSWIN1251'));
Oracle.NoUnicodeSupport := True;

实际上如果应用显式使用TWideStringField,则需要转为TStringField。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-30
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 2019-01-29
    相关资源
    最近更新 更多