我遇到了Delphi中的unicode(宽)字符串字段的问题XE2没有返回字符串的最后一部分,并且任何数据库控件组件也没有完全显示整个字符串到底。
您可以在下面看到简单的测试。
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”组件在发布记录后半个字符后没有保存字符串中的字符。无论如何它只保存了前半个字符串。
有没有办法解决它?
有没有人对如何解决这个问题有任何想法?
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
答案 0 :(得分:2)
您的数据库字符集是什么(来自v$nls_parameters
)?您是否在创建表之前为数据库或会话设置了非默认NLS_LENGTH_SEMANTICS
?
假设您的数据库字符集支持Unicode(即它是AL32UTF8)并且您使用默认的NLS_LENGTH_SEMANTICS
,VARCHAR2(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个字符的字符串。
答案 1 :(得分:1)
我发现了另一个没有任何源代码更改的解决方案。
只需从
更改oracle客户端NLS_LANG参数即可AMERICAN_AMERICA.UTF8
到
AMERICAN_AMERICA.CL8MSWIN1251
并在mainform create event上显式设置params:
Windows.SetEnvironmentVariable(PChar('NLS_LANG'),
PChar('AMERICAN_AMERICA.CL8MSWIN1251'));
Oracle.NoUnicodeSupport := True;
实际上,如果应用程序显式使用TWideStringField,则需要转换为TStringField。