7

我注意到像 Java 这样的语言有一个 char 原语和一个字符串类。Python 和 Ruby 等其他语言只有一个字符串类。这些语言改为使用长度为 1 的字符串来表示字符。

我想知道这种区别是否是由于历史原因。我了解直接影响 Java 的语言有 char 类型,但没有字符串。相反,字符串是使用 char* 或 char[] 形成的。

但我不确定这样做是否有实际目的。我也很好奇在某些情况下一种方式是否比另一种方式更具优势。

为什么像 Java 这样的语言会区分 char 原语和字符串类,而像 Ruby 和 Python 这样的语言却没有?

当然,它必须有某种设计方面的考虑,比如约定、效率、清晰度、易于实现等。语言设计师真的只是从帽子里挑出一个字符表示,可以这么说吗?

4

5 回答 5

4

底线是语言设计者决定如何做到这一点。很难比这更进一步。

然而,关于 C 的一点是,它通常被认为是一种低级语言,因为它的语法更准确地反映了正在执行的数据和任务的性质。将字符视为字符串将是一个抽象级别,这与 C 语言不同。这会使数据在幕后的样子变得不太清楚。当你只需要一个角色时,它几乎肯定会增加开销。

请注意,C 类型语言确实支持单字符串,因此在我看来,您确实拥有两全其美的优点。

于 2013-02-21T19:15:02.280 回答
4

编辑 1添加了许多指向来源的链接;改进了 Lisp 的历史故事;回答了为什么 Java 有原语。 编辑 2评论现代脚本语言,解释效率如何不再是一个问题

在过去,内存很昂贵——即使是简单的计算机也只有几千字节。您必须同意的典型服务条款将超过整个系统的 RAM。这意味着数据结构必须比您今天可以设计要小得多。

计算机于 1940 年代在英国和美国开始出现,这些工程师所需的最小字符集是西欧字母,没有任何令人兴奋的口音。0-9,AZ 和 az 是 62 个字符。添加 31 个控制字符、空格和一些标点符号,您可以将所有内容放入 7 位中。非常适合电传打字机。

现在,这 7 位可以在不同的架构上以不同的方式布局。如果您使用 IBM,则必须知道与ASCII完全不同的EBCDIC

60 年代和 70 年代的语言反映了这些问题,并将字符串打包到尽可能小的空间中:

  • Pascal:压缩的字节数组 - 固定长度且不以 null 结尾
  • C:以空值结尾的字节序列(通常被认为是一个数组,使用了一种疯狂的黑客思想,即数组下标只是指针算术
  • Fortran 66:字符串?你不需要它们。将一对字符存储在一个整数中并使用 READ、WRITE 和 FORMAT

作为这些语言的程序员,我可以说这很糟糕。特别是因为大多数商业程序需要大量的文本输入和操作。随着内存变得更便宜,程序员倾向于先编写字符串实用程序,以便能够做任何有成效的事情。

固定长度的字符串(例如 Pascal)很有效,但如果您需要扩展或收缩它们,即使是单个字符也很麻烦。

C 的空终止方法的缺点是长度不与字符串一起存储,因此很容易覆盖缓冲区并使应用程序崩溃。此类错误仍然是计算机不安全的主要原因。有两种方法可以解决这个问题:

  • 每次写入时检查字符串长度:这意味着扫描内存,直到找到空字符。丑陋
  • malloc新内存并将字符串复制到新内存中,然后free

在 80 年代,越来越多的标准库被引入来处理字符串——这些是由工具供应商和操作系统提供商提供的。标准化有大动作,但各方为了控制标准而争吵不休,很丑陋。

日益国际化也带来了另一个问题——国际字符集。首先,ASCII 被扩展到 8 位作为ISO 8859-1用于不同的欧洲语言(口音、希腊语、西里尔语),然后Unicode将计算机完全带到了世界的各个角落。这带来了UTF-8UTF-16等字符编码以及如何在这些不同方法之间进行转换的问题。

我还应该注意到Lisp引入了垃圾收集。这解决了 C 与malloc/的复杂性free。Lisp 非常强大的数组和序列库可以自然地处理字符串。

将这些趋势结合在一起的第一个主要流行语言是 Java。它结合了语言的三项改进:

  1. 国际化和 Unicode:一种独特的数据类型Character和原语char
  2. 封装:固定长度与空终止的问题通过以下方式消除:
    1. 不可变
    2. VM 和 GC 中的巧妙优化
  3. 库:所有基本的字符串操作功能都在语言中标准化。

如今,有些语言的每个值都是一个对象。然而,当 Java 在 90 年代后期构思时,GC 和 JIT/Hotspot 技术远没有现在这么快(至少部分是由于 RAM 的限制,但算法也得到了改进)。Gosling 关心性能并保留原始数据类型。

还有一点:在 Java 中,有一个 Character 类是很自然的——它是许多操作和实用方法的天然家园,例如isWhiteSpace()and isLetter(),后者在日语、韩语和印度语言中有些复杂。

Python早期决定将字符定义为 8 位 ASCII 是一个糟糕的决定。您可以通过首先引入另一种略有不同且不兼容的数据类型(unicode)来看到随之而来的问题,并且现在只能通过复杂的迁移到 Python 3.x 来解决。

现代语言(包括脚本语言)遵循关于字符串库外观的广泛共识,如 Java 和 Python 所示。

每种语言都是为特定目的而设计的,因此以不同的方式平衡相互竞争的设计问题。现代语言受益于过去 60 年来在性能和内存方面的巨大改进,因此它们更倾向于泛化、纯度和实用性,而不是 CPU 和 RAM 的效率。这对于脚本语言来说是明确的,因为脚本的性质已经做出了这个决定。因此,现代语言往往只有高级字符串类型。

TL/DR早期的计算机在内存方面受到了可怕的限制,不得不采用最简单的实现。现代语言受益于 GC 识别国际化(8bit->16bit)字符并封装字符串数据类型以使字符串操作安全且容易。

于 2013-03-05T08:28:44.013 回答
2

现在,我对此事的看法可能会以一种或另一种形式反映这里的一些答案,但无论如何我都会说:

是的,(就像其他人提到的那样)像 C 这样的低级语言比 Perl、Ruby 或 Python 这样的脚本语言更注重优化、性能和机器级别的细节。现在,这种“完全控制”心态的结果是,与脚本语言相比,您通常需要担心更多的事情。

那我想说什么?好吧,SO 的一个成员曾经给我传过“Python 之禅”,该文档中的一些摘录包含了一些核心 Python 哲学,例如“可读性计数”、“简单胜于复杂”,"There should be one-- and preferably only one --obvious way to do it.我强调了最后一个摘录因为某种原因。

继续前进,这里有一个具有 char 类型的抽象编程语言的示例 SML。以我在交互模式下的两条语句为例:

- val a = "a"
val a = "a" : string    #interpreter type feedback

- val a = #"a"
val a = #"a" : char     #interpreter type feedback

在上面的两个例子中,我有两种方法来表示一个人类可读的字符,尽管它们是根本不同的类型。尽管按照今天的标准,SML 是一种相对抽象的语言,但它的核心理念在于计算、数学表达语法和错误安全。最后一点更适用于纯函数式语言(SML 不是纯函数式)。因此,尽管抽象强调远离低级语言的可怕细节,但它仍然没有像 Python 这样的语言那样强调“可读性”和“易用性”等概念。

事实上,脚本语言通常强调代码的快速生成,以及易于学习和使用的语法。就 Ruby 而言,松本本人甚至宣称该语言应该“使用起来很有趣”。本质上,在我最谦虚的估计中,我认为在 Python 等语言中不区分 char 和 string 数据类型的原因包含在简单性的概念中。冗长和卷积似乎是脚本语言的敌人。此外,最后一点,如果有人如此倾向于使用与 C 兼容的数据类型,那么还有ctypesPython 库。

于 2013-03-05T07:45:55.113 回答
1

我不确定这种区别是否是由于历史原因(C 只有字符,字符串由 char* 或 char[] 组成),或者这样做是否有实际目的。我也很好奇在某些情况下一种方式是否比另一种方式更具优势。

C“字符串”的概念中,是由结束字符终止的字符数组/字符序列\0。否则,“字符串”就像C.

在 egC#和其他几种语言中,字符串被视为抽象,字符串更像是一个不透明的对象。该对象包含对字符串起作用的方法,但字符串的存储方式对程序员来说是“隐藏的”。

原因是 C 是一种更古老的语言,并且比新语言更接近硬件。

如何在语言中定义字符串(无论使用单引号还是双引号)实际上只是设计语言的人当时认为是一件好事的实现细节。

于 2013-02-21T19:39:29.843 回答
1

在 C 和 C++ 中,achar只是一个“小”整数。虽然正如其名称所暗示的那样,它被用于字符编码,但至少在桌面系统或任何需要支持各种语言和字母的系统上面对 Unicode 时,它​​的用途正在减少。然而,由于这些是能够直接访问硬件的“系统级”语言,因此还必须具有能够寻址特定架构上最小的可寻址内存单元的数据类型;这就是为什么 achar是必要的。

C# 区分char用于字符编码的类型(实际上是 16 位)和最小的可寻址单元类型byte,即 8 位。这种清晰也许是晚会的优势。

C 当然实际上根本没有字符串数据类型,它只有一个以 nul 结尾的字符数组的约定和一个使用该约定的函数库(顺便说一下,这是一个简单但效率低下的约定,如这里所解释的)。在 C++ 中,字符串类带来了真正字符串类型的优点,并且可以避免一些低效率和危险——尽管减轻危险会增加其自身不同的性能影响)。

于 2013-02-21T21:17:55.140 回答