使用 resourcestring 与使用 const string 相比有什么优势?

24

请问在使用下列代码时是否有一些优势(例如更少的存储空间、提高速度等):

resourcestring
    MsgErrInvalidInputRange = 'Invalid Message Here!';

取代

const
    MsgErrInvalidInputRange : String = 'Invalid Message Here!';
5个回答

17

使用const选项比resourcestring更快,因为后者将调用Windows API获取资源文本。您可以通过使用一些缓存机制来加快速度。这就是我们在Enhanced Delphi RTL中所做的。

如果您需要多次访问resourcestring内容,那么首先将其加载到字符串中是一个好主意。

resourcestring的主要目的是允许程序进行i18n(国际化)。

您可以在某些Delphi IDE版本中使用翻译管理器。但它依赖于外部DLL。

您可以使用从Linux世界引入的gettext系统,即http://dxgettext.po.dk,它依赖于外部.po文件。

我们在框架中包含了自己的i18n机制,它可以翻译和缓存资源字符串文本,并依赖于外部的.txt文件(您可以使用UTF-8或Unicode文本文件,从Delphi 6到XE)。缓存使其与const用法一样快。请参见http://synopse.info/fossil/finfo?name=SQLite3/SQLite3i18n.pas

还有其他开源或商业解决方案。

关于存储大小,resourcestring被存储为UC2缓冲区。因此,在Delphi 2009之前,resourcestring将比string使用更多的内存。自Delphi 2009以来,所有字符串都是unicodestring,即UCS2,因此您不会有更多的存储空间。在所有情况下,文本的存储大小不是应用程序的最大大小参数(位图和代码大小对最终exe有更大的影响)。


如果资源系统未初始化,它是否会实际调用Windows API?(即没有加载任何翻译)我没有深入了解Delphi,但FPC不会。dxgettext很棒(我更喜欢它而不是ITE),但它也有缺点,特别是在资源字符串方面。 - Marco van de Voort
@Marco:当然,即使没有加载翻译,Windows API 也总是被调用的。请检查 System.pas 中的 LoadResString() 过程。 - Arnaud Bouchez

7

资源字符串存储在exe资源的STRINGTABLE条目中,常量存储为固定数据段的一部分。由于它们是资源部分的一部分,因此您可以提取它们和DFMs,将其翻译并存储在资源模块(仅数据DLL)中。当Delphi应用程序启动时,它会查找该DLL,并使用其中的字符串来加载翻译,而不是使用包含在EXE中的字符串。

Embarcadero docwiki介绍了如何使用Translation Manager,但许多其他Delphi翻译工具也使用资源字符串。


2
资源是Windows下本地化应用程序的事实标准。翻译编辑器和其他工具还可以修改存储为资源的.dfms文件。优点是不仅可以定制字符串,甚至可以定制控件大小、位置、颜色和图像以适应目标“文化”,这是使用仅翻译字符串的工具无法完成的。 - user160694
FPC 2.4.0+ 支持在任何地方使用 resourcestrings 的机制。据我所知,Lazarus 还没有将它的 dfm 存储在资源中,但这可能会在短期内改变,尽管 Lazarus 仍然正在经历从 2.2 到 2.4 的过渡。 - Marco van de Voort
@ldsandon:您可以在不进行任何资源修改的情况下,动态自定义resourcestring和dfm。这就是dxgettext和我们的单元所做的事情,通过挂钩一些VCL调用(如LoadResString或TCustomForm.Create)来实现。 - Arnaud Bouchez

6
正如其他人提到的那样,resourcestring字符串将包含在您的exe中的单独资源中,因此在需要为应用程序的多语言界面提供支持时具有优势。正如一些人所提到的那样,const字符串包含在应用程序的数据部分中。
D2007之前的Delphi版本中,const字符串被存储为Ansi字符串,每个字符需要一个字节,而resource字符串将存储为UTF-16:Windows默认编码(尽管可能不适用于Win9x)。我记得D2007及以前的版本不支持UTF-8编码的单元文件。因此,源代码中编写的任何字符串都必须受到ANSI代码页的支持,并且很可能不会超出Unicode基本多文种平面。这意味着只使用了UTF-16的UCS-2部分,并且所有字符串都可以以每个字符两个字节的方式存储。
简而言之,D2007之前的const字符串每个字符需要一个字节,resource字符串每个字符需要两个字节。
从D2009开始,Delphi启用了unicode。自那时起,情况有所不同。Resourcestring字符串仍然存储为UTF-16。没有其他选择,因为它们由Windows“管理”。
然而,Consts字符串是完全不同的故事。自D2009以来,Delphi在您的exe中存储每个const字符串的多个版本。每个版本都有不同的编码。Const可以存储为Ansi字符串,UTF-8字符串和UTF-16字符串。
存储这些编码中的哪一个取决于const的使用方式。默认情况下,将使用UTf-16,因为它是Delphi的默认内部编码。将相同的const分配给“常规”(UTF-16)字符串以及AnsiString变量,const将同时以UTF-16和Ansi编码存储在exe中...
从实验结果来看(使用D5和D2009),Delphi对const字符串进行了去重处理,而对resourcestring字符串则没有这样做。

4
去重位是逻辑性的。在“应用程序内置语言”中相同的两个词可能会有多种不同的含义,具体取决于翻译的上下文。 - Marco van de Voort
“每个字符一个字节”和“每个字符两个字节”的说法在MBCS(如亚洲)字符集中并不正确。但对于英语/西欧语言(代码页1252),这是正确的。无论哪种情况,正如我在答案中所述,资源字符串大小对最终exe文件大小并不重要(位图和代码更为重要)。 - Arnaud Bouchez
@A.Bouchez:确实如此,对于Delphi版本直到D2007,在使用MBCS代码页时,每个字符的字节数不会有任何区别。 - Marjan Venema

2

使用resourcestring,编译器会将这些字符串作为一个字符串表资源放在可执行文件中,这样任何人(比如你的翻译团队)都可以使用资源编辑器进行编辑而无需重新编译应用程序或访问源代码。


1

还有第三个选项:

const MsgErrInvalidInputRange = '无效的消息!';

后者应该更高效,因为它告诉编译器不要在数据段中分配空间,而是可以将字符串放在代码段中。另外,请记住,使用类型常量所能做的取决于$WRITEABLECONST指令,尽管我不知道编译器何时打开或关闭。


不错!我错过了原帖中常量的输入。键入常量与真实常量相比的开销是指针解引用。但是,两个字符串都将进入.data部分。将一些数据放入代码段需要 asm S db 'foo' 并使用一些技巧将 OFFSET S 传递到外部作用域。如果我没记错的话,可写类型常量和初始化变量是完全等价的。 - Free Consulting
在代码段中存储数据是可能的,只要该段可读且不仅限于可执行。我不知道Delphi编译器是否仍然这样做,但我确定旧的TP编译器是这样的。此外,据我所知,真正的常量和类型常量的存储和处理方式并没有得到记录。 - user160694
这不会“更高效”。如果存储在数据或代码段中,它将被完全相同地编码。 - Arnaud Bouchez
缓存性能很可能会偏向于将字符串声明在使用它的代码旁边。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接