你将如何实现你的字符串类型?

时间:2009-02-19 17:53:08

标签: string

假设您正在从头开始设计和实现一种新语言,尽管您可以从现有语言/实现中自由借鉴。

问题:如果程序员声明了一个字符串变量(假设是强类型的),你会如何选择将这个变量存储在内存中?

有许多用例,但您是否拥有某些特定型号的特定型号?你的字符串是否可变?它是可变的,但只是一段不是记忆结束的长度吗?我可以动态设置长度,还是只能在编译时完成?是否容易访问'nth'元素?字符串是否需要连续的内存扇区?它可以分解成更小的字符串吗?

程序员可能会喜欢用字符串来考虑某些事情: 计算长度。 添加到字符串。 提取字符串的部分(子串)。 应用正则表达式。 转换为不同的值(数字,布尔值等)

编辑:澄清我的意思。

如果用户声明以下内容:

var Name : string

作为语言设计师,您如何选择如何将其存储在RAM中?您的方法有哪些优点和缺点。

6 个答案:

答案 0 :(得分:5)

如果我从头开始编写语言,我希望定义可变和不可变的字符串类型。不变性使得字符串处理操作更快,但会产生严重的限制,特别是在连接等方面。

不可变字符串,我将存储为以null结尾的unicode值数组。可变字符串,我将存储为unicode字符的链接列表,以便于重新洗牌,切片等。

答案 1 :(得分:3)

我会避免使用C字符串。计算长度是O(n)。共享子串几乎是不可能的。连续的内存需求导致碎片化。终结器的任何问题都会导致错误和安全漏洞。如果将其存储为UCS-4,则会浪费大量的ASCII字符串空间(并且失去C兼容性,这是C字符串的一个好处);如果将其存储为UTF-8,则索引为O(n)。当您在裸PDP-11上编写ASCII库时,PDP-11的ASCIZ类型才真正有用。

比PDP-11更年轻的语言通常使用不同的结构:

  • Pascal使用长度字段而不是终结符 - 它们的strlen()是O(1)。
  • Forth使用(地址,长度)加倍 - 他们的strlen()是O(1),加上他们可以轻松共享子串。
  • 像Java这样的许多现代“托管”语言也分别存储长度。
  • 在其他语言(如Common Lisp)中,字符串只是vector的子类型(其元素是字符)。
  • Excel团队使用了C语言,但为了提高性能而实现了自己的Pascal字符串。

我会使用ropes之类的东西。连接是恒定时间。它们不需要连续的内存。子串共享很容易。无需在多线程环境中锁定即可执行所有操作。也许允许UCS-4和ASCII节点在常见情况下使存储更紧凑,和/或让它在内部自动使用更简单的结构来实现真正的短字符串。

如果您的内存很少,短字符串,7位字符,可信输入,并且您的CPU太慢以至于您的程序员时间非常小心,那么ASCIZ非常棒。在Unicode,多线程,高效GC,快速CPU和大(可能是不可信)输入的现代世界中,它不再是一个很好的选择。

答案 2 :(得分:2)

我假设你的意思就像你在设计一种语言一样?然后我想我会使用C的模型并将其存储为连续的内存,null终止。这对我来说似乎是最合乎逻辑的方式。

优点:如果您对空值进行折扣,则不会浪费内存。

缺点:必须通过方法等计算字符串长度

答案 3 :(得分:2)

我首先要求附加到字符串的编码。如果未在源中指定,则字符串文字将具有与源文件本身相同的编码。

当然我偏向于UTF-8,并且可能会安排标准库在该机制中运行

另外,我考虑使用比字节数组更聪明的结构表示,因为谁想要使用缓冲区麻烦!

SGI的模板库附带了一个“绳索”抽象类型,可以很好地完成这一任务。迭代器(但不是迭代)很昂贵,但作为交换,插入,删除,附加,子范围和比较都非常便宜。

Lua编程指南中有另一个很好的实现,它实现了一个“河内塔”优化,非常适合迭代地从前到后构建字符串,就像读取大文件时经常做的那样。 p>

TCL通过文本字段小部件间接实现此目的。它甚至使文本注释通常有用。唯一的缺点是这种设计对于没有面向行分布的序列效果不佳。

使用不可变字符串的主要原因是动态或解释语言为自己使用字符串。它真的使用原子,它们是任意的,但需要可以转换为字符串和从字符串转换。 Lisp显式地使用符号常量与字符串分开。我喜欢这个,尽管我并不喜欢lisp。

答案 4 :(得分:1)

我相信你问的是如何实现一个字符串对象。

出于性能原因,您需要将为字符串中的字符分配的内存保留为单个块。这将加速对所有元素执行的操作 - 大小写更改,复制,长度计算,索引等。它还可以更容易地实现在字符串的开头或结尾处理的操作 - trim,substring等。

某些操作会使链接列表等数据结构更容易实现,例如插入或删除字符/子字符串。但是,考虑到维持这种数据结构的内存开销与平均字符串长度的比率,成本超过了任何潜在的收益。

字符串是否应该是不可变的取决于两个考虑因素:

  • 您是否可以直接访问内存存储,或者是否所有操作都封装在一个类中?
  • 是由运行时管理的内存分配,还是你必须自己管理它?

传统的C ++方法是将底层内存直接访问到使用字符串对象的代码。这很有意义,因为无论如何内存都由客户端代码分配和管理,因此直接访问它可以提供最佳性能。缺点是任何改变字符串长度的操作通常都会导致重新分配内存。有一些智能字符串类可以阻止他们自己的内存管理器来处理这个问题,比如ATL的CString。

C#方法是将底层内存封装在对象中,并使字符串不可变。这允许内存由CLR管理,并且对象通过适用于任何其他对象的相同规则进行垃圾收集。由于这一点,有一个很小的性能损失,但简化使用的好处和提供相当复杂的操作的稳定实现的能力超过了perf的成本。另外,附带的StringBuilder类提供了直接访问内存的一些优点,它通过预先分配更大的缓冲区并改变其中的实例,直到它最终确定为String实例。

答案 5 :(得分:0)

我会解析.NET String类并从中构建。