如何比较包含非英语字符的unicode字符串以进行alpabetically排序?

csh*_*tor 18 delphi unicode collation

我试图根据包含非英文字符的unicode字符串值对数组/列表/任何数据进行排序,我希望它们按字母顺序正确排序.

我写了很多代码(D2010,win XP),我认为这对于未来的国际化非常可靠,但事实并非如此.它全部使用unicodestring(string)数据类型,到目前为止我刚刚将英文字符放入unicode字符串中.

我似乎不得不承担起非常严重的unicode错误.我跟我的德国朋友聊了一下,尝试了一些德语ß,(ß是's',应该在S之后和T之前的字母表中)和ö等(注意变音符号),我的排序算法都不再适用了.结果非常混乱.垃圾.

从那以后,我一直在广泛阅读并学习了很多关于unicode整理的不愉快的事情.事情看起来很严峻,比我想象的要严重得多,我已经严重搞砸了.我希望我错过了一些东西,事情实际上并不像现在看起来那么严峻.我一直在修补Windows api调用(RtlCompareUnicodeString)没有成功(保护错误),我无法让它工作.我学到的API调用的问题是,他们在各种较新的Windows平台上进行了更改,并且很快就会使用delphi进行交叉平台,随后使用linux,我的应用程序是客户端服务器所以我需要关注这一点,但是这种情况是什么是不是(糟糕)我会感激任何前进的进步,即赢得api具体.

使用win api函数RtlCompareUnicodeString来明显解决方案吗?如果是这样的话,我应该再次尝试,但是我已经对unicode整理所涉及的所有问题感到吃惊,我不知道我应该采取什么方式来比较这些字符串.

我了解了IBM ICU c ++开源项目,虽然是旧版本的ICU,但它有一个delphi包装器.这似乎是一个非常全面的解决方案,它是独立于平台的.当然,我不能为此创建一个delphi包装器(或更新现有的包装器)以获得unicode整理的良好解决方案?

我很高兴听到两个层面的建议: -

A)Windows特定的非便携式解决方案,我很高兴此刻,忘记了客户端服务器的后果!B)一个更便携的解决方案,不受各种unicode api功能的各种XP/vista/win7变种的影响,因此使我在XE2 mac支持和未来的Linux支持方面处于有利地位,更不用说客户端服务器的复杂性了.

顺便说一句我真的不想做'make-do'解决方案,在比较之前扫描字符串并替换某些棘手的字符等,我已经读过了.我给出了上面的德语示例,这只是一个例子,我想让它适用于所有(或至少大多数,远东,俄语)语言,我不想为特定语言或两个语言做变通办法.我也不需要任何关于排序算法的建议,它们很好,它只是字符串比较位错了.

我希望我失踪/做一些愚蠢的事情,这一切看起来都很头疼.

谢谢.


编辑,鲁迪,这是我试图调用RtlCompareUnicodeString的方式.对不起,我已经度过了一段可怕的时光.

program Project26

{$APPTYPE CONSOLE}

uses
  SysUtils;


var
  a,b:ansistring;

  k,l:string;
  x,y:widestring;
  r:integer;

procedure RtlInitUnicodeString(
  DestinationString:pstring;
  SourceString:pwidechar) stdcall; external 'NTDLL';

function RtlCompareUnicodeString(
  String1:pstring;
  String2:pstring;
  CaseInSensitive:boolean
  ):integer stdcall; external 'NTDLL';


begin

  x:='wef';
  y:='fsd';

  RtlInitUnicodeString(@k, pwidechar(x));
  RtlInitUnicodeString(@l, pwidechar(y));

  r:=RtlCompareUnicodeString(@k,@l,false);

  writeln(r);
  readln;

end.
Run Code Online (Sandbox Code Playgroud)

我意识到这很可能是错的,我不习惯直接调用api notctions,这是我最好的猜测.

关于StringCompareEx api函数.这看起来真的很好,但只对Vista +有用,我正在使用XP.StringCompare是在XP上,但那不是Unicode!

回顾一下,基本任务是比较两个字符串,并根据当前Windows语言环境中指定的字符排序顺序进行比较.

任何人都可以肯定地说ansicomparetext是否应该这样做?它对我不起作用,但是其他人已经说过应该这样,而我读过的其他内容也表明应该这样做.

这是我在德语语言环境中使用AnsiCompareText时得到的31个测试字符串(空格分隔 - 没有字符串包含空格): -

  • arßAsßasßaßsnonööööoeoööööööööösössSSSaßßbßsßSasaSßbßßssssSSSSßßßssßßSSßßßzßzßzzzz

编辑2.

我仍然希望听到我是否应该期待AnsiCompareText使用区域设置信息,就像lkessler所说的那样,并且lkessler之前也发布了关于这些主题的内容,并且之前似乎已经完成了这些.

然而,根据Rudy的建议,我也一直在检查CompareStringW - 它与CompareString共享相同的文档,所以它不是我之前说过的非unicode.

即使AnsiCompareText无法工作,虽然我认为应该如此,但win32api函数CompareStringW确实可以工作.现在我已经定义了我的API函数,我可以调用它,我得到一个结果,没有错误......但是无论输入字符串如何,我每次都得到相同的结果!它每次返回1 - 这意味着小于.这是我的代码

var
  k,l:string;

function CompareStringW(
  Locale:integer;
  dwCmpFlags:longword;
  lpString1:pstring;
  cchCount1:integer;
  lpString2:pstring;
  cchCount2:integer
  ):integer stdcall; external 'Kernel32.dll';

begin;

  k:='zzz';
  l:='xxx';

  writeln(length(k));
  r:=comparestringw(LOCALE_USER_DEFAULT,0,@k,3,@l,3);

  writeln(r); // result is 1=less than, 2=equal, 3=greater than
  readln;

end;
Run Code Online (Sandbox Code Playgroud)

经过多次痛苦,我觉得自己现在到了某个地方.很高兴知道AnsiCompareText,以及上面的CompareStringW api调用我做错了什么.谢谢.


编辑3

首先,我自己修改了对CompareStringW的api调用,当我应该做PString(mystring)时,我传入@mystring.现在一切正常了.

r:=comparestringw(LOCALE_USER_DEFAULT,0,pstring(k),-1,pstring(l),-1);
Run Code Online (Sandbox Code Playgroud)

现在,你可以想象我仍然感到沮丧,因为我仍然得到了与我在开始时做的相同的结果......

  • arßasßaßsAsßnonööööoeoööööööööösösssSSSßaSßbßsssssSSSSßßsssßßSSßßßzßzßzzzz

您可能还会想到,当我意识到排序顺序是正确的时候,我的EXTREME沮丧,更不用说同时的快乐了,并且它在开始时正确地重新开始!说它有点不舒服,但首先从来没有任何问题 - 这完全取决于我缺乏德国知识.我认为这种错误是错误的,因为你可以看到上面的字符串以S开头,然后它们以ß开始,然后再次返回ß,依此类推.好吧,我不会说德语,但我仍然可以清楚地看到他们没有正确排序 - 我的德国朋友告诉我ß来自S之后和T之前......我错了!发生的事情是字符串函数(AnsiCompareText和winapi CompareTextW)每个'ß'都用's'替换,而每个'ö'都有一个正常的'o'......所以如果我把这些结果带到搜索结果并进行搜索并按照描述更换我得到......

  • arss asss asss Asss no no o o o o o ooo ooo op s ss SS ssass ssbss sss sssa ssss ssss ssss SSSS ssssss ssssss SSssss ssz sszss z zzz

看起来对我来说很正确!它一直都是.

我非常感谢所有给出的建议,非常抱歉浪费了你的时间.那些德语ß让我感到困惑,内置的delphi函数或其他任何内容都没有任何问题.它看起来就像是.我错误地将它们与我的测试数据中的普通's'结合起来,任何其他字母都没有创造出这种无法排序的错觉!波浪形的ß让我看起来很傻瓜!SSS!

Rudy和lkessler我们都特别有帮助,两个人,我必须接受lkessler的回答最正确,对不起Rudy.

Rud*_*uis 9

您说您自己调用Windows API调用时遇到问题.你可以发布代码,所以这里的人可以看到它失败的原因吗?它并不像看起来那么难,但确实需要一些关心.ISTM RtlCompareUnicodeStrings()太低了.

我找到了一些解决方案:

非便携

您可以使用Windows API函数CompareStringEx.这将使用Unicode特定的排序规则类型进行比较.您可以指定完成此操作的方式(请参阅链接).它确实需要宽字符串,即PWideChar指向它们.如果你在调用它时遇到问题,请给出一个声音,然后我会尝试添加一些演示代码.

或多或少的便携性

为了使这或多或少可移植,您可以编写一个比较两个字符串的函数,并使用条件定义来为平台选择不同的比较API.


lke*_*ler 7

如果您希望在任何语言环境中排序完全相同,请尝试使用CompareStr表示区分大小写,或者使用CompareText表示不区分大小写.

如果您希望自己的排序特定于用户的语言环境,则使用AnsiCompareStr表示区分大小写,或使用AnsiCompareText表示不区分大小写.

请参阅:如何在Delphi中TStringList进行不同的排序,以获取更多相关信息.