在您尝试之前,请不要讨厌 COBOL

COBOL 就像计算机编程界的 Rodney Dangerfield,它仍然在使用中,并且确实值得尊敬。
485 位读者喜欢这篇文章。
What to like about COBOL

Rainer Gerhards。由 Opensource.com 修改。CC BY-SA 4.0

COBOL 是编程语言界的 Rodney Dangerfield——它不受任何尊重。它经常因其冗长而被贬低,并被斥为过时。然而,COBOL 远非一种死语言。据估计,它处理了所有业务交易的 85%,每年编写 50 亿行新的 COBOL 代码。

我做了 10 年的 COBOL 程序员,我不认为它像人们口口相传的那样糟糕。事实上,它非常擅长处理货币和固定格式的记录。但是 COBOL 确实有它的怪癖,其中许多怪癖都植根于早期编程的计算环境。这是一个关于穿孔卡片如何吃掉我的程序的故事。

一个神秘的错误

这是一个问题代码的示例,它尝试计算订单的运费和预计发货日期

 1      identification division.
 2      program-id.
 3          test-ship.
 4
 5      data division.
 6      working-storage section.
 7
 8      01 shipping-method            pic x(2) value 'US'.
 9      01 cust-type                  pic x(2) value 'EM'.
10      01 normal-ship-date-yyyymmdd  pic 9(8) value 20170522.
11      01 nextday-ship-date-yyyymmdd pic 9(8) value 20170508.
12      01 expected-shipping-date     pic 9(8).
13      01 shipping-charge            pic 99v99 value 4.99.
14
15      procedure division.
16          if shipping-method <> 'FX'
17              move normal-ship-date-yyyymmdd to expected-shipping-date
18          else
19              move nextday-ship-date-yyyymmdd to expected-shipping-date.
20
21          if cust-type = 'EM'
22              move 0 to shipping-charge.
23
24          display expected-shipping-date.
25          display shipping-charge.

即使您以前从未见过 COBOL 代码,逻辑也应该很容易理解。如果运输方式是“FX”,客户将获得次日达服务,否则运输需要两周时间。(那是 90 年代。)员工享受免费送货;其他所有人支付 4.99 美元。这段代码在我看来是正确的,但它有一个错误——发货日期计算正确,但员工被收取全额运费。

问题最终出在第 19 行末尾的句点。那时,追踪这个问题需要一些侦探工作,但现代语法高亮编辑器会立即标记出来。但是为什么这是一个问题?为什么 COBOL 不喜欢那个句点,而它对第 22 行末尾的句点却非常满意呢?

句子而不是代码块

要回答这个问题,我们需要追溯到 COBOL 在 1950 年代后期的起源。在此之前,大多数语言都是为解决科学和工程问题而设计的,因此它们的语法类似于数学方程式。(Fortran 是这种类型语言的经典示例。)另一方面,COBOL 旨在用于商业计算。为了让外行人更容易学习,Grace Hopper 和她的国防部和 IBM 工程师团队赋予 COBOL 英语语言语法。COBOL 程序具有分层结构,而不是大多数现代语言都具有的递归语法。COBOL 将语句组合成“句子”而不是代码块。就像英语一样,每个句子都以句点结尾。

虽然这在理论上看起来是个好主意,但在实践中却被证明是有问题的。它使得移动代码变得困难,因为一个不小心出现的句点可能会意外地终止一个代码块。句点也很难注意到——在 90 年代早期的 CRT 终端上,它们通常只是一个像素。但这里存在一个更深层次的问题,这个问题与程序员在 COBOL 首次开发时编写代码的方式有关。

穿孔卡片

COBOL punch card

opensource.com

当 COBOL 被设计出来时,硬盘驱动器的价格非常昂贵,因此大多数程序都是在穿孔卡片上编写的。最常见的穿孔卡片由 12x80 的网格组成,其中一个孔代表 1,一个非孔代表 0。每列是一个 12 位的字符,每张卡片是一行 80 个字符的文本。要运行您的程序,您需要将一叠穿孔卡片送入读卡器。每张卡片的前六列和最后八列保留用于序列号和标识符。这样,如果您不小心掉落了卡片堆(这可能是您程序的唯一副本),您可以将卡片通过机械分拣机重新按正确的顺序排列。

这意味着 COBOL 会忽略第 72 列之后的任何字符。如果第 72 列之后恰好是一个句点,那么您代码的整个逻辑都可能发生变化。而且,正如您现在毫无疑问猜到的那样,第 19 行的句点在第 73 列。以下是 COBOL 编译器实际解释这些行的方式

16          if shipping-method <> 'FX'
17              move normal-ship-date-yyyymmdd to expected-shipping-date
18          else
19              move nextday-ship-date-yyyymmdd to expected-shipping-date
20
21              if cust-type = 'EM'
22                  move 0 to shipping-charge.

一旦我发现问题所在,修复就很容易了:我从第 19 行的开头删除了一个空格字符,这将句点放在了第 72 列。虽然我以前从未遇到过这种情况,但这却是一个非常常见的错误,以至于许多大型机 COBOL 程序员会在他们的终端上在第 72 列和第 73 列之间粘贴一条线。

今天的 COBOL

COBOL-85 标准添加了诸如 end-if 之类的范围终止符,因此不再需要句点来结束句子。COBOL 2002 标准允许自由格式代码,尽管许多编译器在此之前很久就支持了。按照 2002 标准编写的相同代码看起来更像是一种现代编程语言

16 if shipping-method <> 'FX'
17     move normal-ship-date-yyyymmdd to expected-shipping-date
18 else
19     move nextday-ship-date-yyyymmdd to expected-shipping-date
20 end-if
21
22 if cust-type = 'EM'
23     move 0 to shipping-charge
24 end-if

请注意,行首的空格也不再是必需的。我通常使用的系统同时支持范围终止符和自由格式代码,因此直到我不得不在另一个系统上进行一些更改时,我才遇到这个问题。

开源爱好者学习 COBOL 一直很困难。COBOL 编译器传统上是闭源且昂贵的,并且大多数 COBOL 代码都是在公司环境中编写的。然而,一个名为 OpenCOBOL 的开源编译器的开发工作始于 2002 年。2013 年,它被正式接受为 GNU 软件包并更名为 GnuCOBOL。要了解有关 GnuCOBOL 的更多信息,包括访问 400 页的程序员指南,请访问该项目的主页

在 Walt Mankowski 的演讲一张穿孔卡片吃掉了我的程序!中了解更多关于 COBOL 的信息,该演讲将于 8 月 26 日在费城的 FOSSCON 上进行。

标签
User profile image.
Walt Mankowski 是一位从象牙塔计算机科学家转型而来的人,他最近完成了一项博士后研究,与生物学家合作处理和可视化 TB 级的 2D 和 3D 时间流逝显微镜图像。在他的过去的生活中,他曾在一家大型有线家庭购物网络公司担任了 10 年的 COBOL 程序员。他喜欢 Perl、正则表达式、高性能计算和《飞出个未来》。

39 条评论

感谢这篇精彩的文章!我在职业生涯的早期做过一些 COBOL 工作,并且仍然是它的粉丝。

我已经使用 COBOL-85 标准三十年了,从来没有遇到过句点问题。事实上,我写的每个段落都以 EXIT 语句结尾,段落中的最后一件事是 EXIT 前一行上的 PERIOD。

在您编写过程划分代码时要有条理;在您的 WORKING-STORAGE SECTION 中要有创新性和条理性,使用 COBOL 您没有什么不能做的。

1000-PARAGRAPH.

IF
blah blah blah
END-IF
.
1000-EXIT. EXIT.

:)

只是一个小小的吹毛求疵... 第 72 列很特别。如果它包含连字符或“C”,那么编译器应该将下一行附加为单行进行处理。

老实说,我 99% 的时间使用的系统都使用自由格式代码,所以这从来没有出现过。但我在线找到的所有内容都表明,延续是由第 7 列中的“-”表示的,这意味着当前行应该附加到前一行。我找不到任何表明第 72 列被特殊处理的证据。也许您将其与另一种语言混淆了?

回复 作者 Martin DiViaio (未验证)

抱歉... 但绝对是第 72 列。已经过去 30 多年了,所以我忘记了它是 COBOL66 的一部分还是 ANSI COBOL 74“标准”。我记得有人曾经尝试使用它,直到系统管理员告诉他使用更短的变量名并继续前进。

回复 作者 waltman

自 1983 年以来,我一直是 IBM 大型机软件开发人员。特殊的是第 7 列,而不是第 72 列。“-”表示从上一行延续,“*”表示注释行,“/”是编译器指令,用于在编译器列表中强制分页符。任何其他内容都不是标准。

回复 作者 Martin DiViaio (未验证)

看来 JCL 和一些大型机汇编程序使用第 72 列来表示延续,所以我猜想某些早期的 COBOL 版本也可能这样做。

回复 作者 Scott Spurlock (未验证)

如果我没记错的话,早在 1960 年代,硬盘驱动器在 COBOL 首次问世时并不存在。大型计算机将数据保存在磁带或纸带上。

IBM 在 1956 年发布了他们的第一个硬盘驱动器 IBM 305 RAMAC。那是在 COBOL 之前几年,COBOL 的历史可以追溯到 1959 年。硬盘存储非常昂贵,这可能就是它们仍然不常见的原因。

回复 作者 Greg P

当我查找它时,硬盘驱动器似乎确实存在,但尚不清楚它们是如何使用的,因为它们最初通常只有几 MB 的存储空间。

回复 作者 Greg P

是的。在我的演讲中,我有一张 1956 年工人们搬运一个 5 MB 硬盘驱动器的照片,它有几个冰箱那么大。但是,我用过的第一个 PC 硬盘驱动器只有 3 MB 的存储空间,但它看起来已经非常大了!

回复 作者 Greg P

这叫做臃肿软件。今天的计算机和手机都充满了它。

在吸毒时代(60 年代和 70 年代),您有时只有 40 KB 的大型机空间来运行程序。
所以您非常努力地使其适应。如果 COBOL 做不到,那么您就用汇编程序来做(至少我们中的一些人是这样做的)。
与今天的热门人物不同,只有强者才能生存。

回复 作者 Greg P

我在 80 年代早期开始在那些早期的 PC 上编程。当我还是象牙塔计算机科学家时,我仍然有一台 PC,但它有 32 个 CPU、64 GB 的 RAM 和 TB 级的存储空间。我们能够处理当时无法想象的计算问题。而且,编程已经够难了,不必再微观管理内存。虽然回顾过去很有趣,但这让我更加欣赏现代硬件和软件!

回复 作者 Tony Q. King

在您看到 COBOL 程序在配备 12 个大型磁带驱动器的 4K Remington-Rand Univac-II 上编译之前,您什么都没见过,而且所有磁带驱动器都在同时旋转。在 1960 年代初期,一个小程序需要大约 1 到 2 个小时才能编译。
但是一旦 IBM 360 在 1965 年左右推出,配备了大型 2311 磁盘驱动器 - 每个 7 Meg!- 那个程序将在不到 10 分钟内编译完成。

是的,那是 4K,而不是 4 Meg。

回复 作者 Greg P

磁盘驱动器在 60 年代中期随着 IBM 的 360 系列计算机和操作系统的发布而变得普遍。对于试图在配备 4K RAM 和 12 个大型磁带驱动器的 Remington-Rand Univac-II 上编译 COBOL 程序的人来说,这是一种受欢迎的解脱。
您应该看到所有这些磁带在编译一个小型 COBOL 程序时都在运行。通常需要 1 到 2 个小时(自然是在夜间运行)
(嘟囔嘟囔... 诅咒你,Grace Hopper...!)

** 是的,那是 4 K!不是 4 MB。

回复 作者 Greg P

很久以前用过 Cobol,现在仍然喜欢它!简单易懂。如果我今天想编写一行代码,我必须安装 25 个开源工具和库,破解一些命令行东西,并在任何东西出现之前祈祷。

当您在第 7 列中输入星号时,它会将该行的其余部分变成注释。你猜怎么着,我有一个注释掉了一个 GOTO 语句。(哎呀)

第 7 列和第 72 列协同工作。第 7 列有几个用途,但它可以与第 72 列结合使用,以表示延续和连接几个涉及国家字符集(解析为非拉丁字符代码的多字节序列)的特殊情况。请参阅:https://www.ibm.com/support/knowledgecenter/en/SS6SGM_3.1.0/com.ibm.aix…

关于穿孔卡片的另一个次要观察或琐事。
今天很少有程序员/ IT“专家”意识到 80 个字符的文本行(在编辑器中或在终端屏幕上)来自 120 年前(至少!)的 Hollerith 穿孔卡片,最终被 IBM 数据处理采用,然后在 1970 年代编程库最终进入磁带(短暂地)然后进入磁盘。
唉,Herman Hollerith 在 COBOL 甚至在 Grace 海军上将的 80 列卡片上打孔之前很久就去世了。
如果您想为此辩护,您甚至可以争辩说,这些穿孔卡片最初是在 18 世纪由纺织织工开发的。

附录:在任何人试图追溯更久远的历史之前
有些人声称穿孔卡片的宽度(或长度)最初是基于罗马战车车轮的车辙宽度及其 2000 年前罗马道路上特有的矩形压痕。
胡说八道!与关于 Saturn-1 火箭助推器尺寸基于罗马道路的说法一样,这只是另一个都市传说。;-)

回复 作者 Tony Q. King

硬盘驱动器的有限容量和成本与穿孔卡片的使用(在存储程序方面)有关,但也有缺乏廉价终端的问题。在 1970 年代后期的马里兰大学,学生可用的 ASCII 电传打字机数量有限,这意味着绝大多数学生不得不使用穿孔卡片。(当我在那里时,电传打字机被类似 DECwriter 的点阵键盘/打印机取代了。)UNIVAC 大型机的磁鼓已被硬盘取代,但操作系统继续提供在硬盘上模拟磁鼓的鼓 API。

我只在 1981 年的数据库课程中使用过一次 COBOL,但我之前几年读过 Daniel McCracken 关于 COBOL 的经典著作(以及他关于 Fortran 和 Algol 的书)。我使用 COBOL 没有任何问题;每种编程语言都需要不同的思维方式,了解 COBOL(在有限的时间内)与了解我使用过的所有其他语言一样令人在智力上感到满足。

在 1990 年代,我认为,一位 COBOL 布道者过去经常在美国新闻组 comp.os.unix 或 comp.lang.c 上发帖。他主要关注的不是 COBOL;它只是偶尔出现。在一篇文章中,他提出了一个*完整*、*可移植*的 4 行 COBOL 程序来排序文件。(我记得是这样...)可移植性是可能的,因为 COBOL 内置了排序功能,而大多数其他语言则没有。在另一种语言中使用特定于操作系统的系统调用来运行外部“sort”程序不是一种可移植的解决方案。

我在 80 年代早期去了宾夕法尼亚大学。我从未使用过穿孔卡片,但仍有很多旧硬件正在逐步淘汰。但我真正谈论的是 COBOL 的早期。计算机终端直到 70 年代初才开始可用,而 COBOL 在那时已经使用了十年。电传打字机出现得更早,但我不确定它们与大型机配合使用效果如何,因为我见过的所有电传打字机都需要双同步连接。

今天,当我使用 C++、Perl 或 Python 等语言进行编程时,我经常会想是否有一种更好的方法可以编写某些东西。我应该使用哈希还是对象?结构还是类?循环还是列表推导式?我从不在 COBOL 中这样做,因为通常只有一种方法可以做任何事情。它可能不是一个好方法,但这就是你的做法。因此,我的大部分精力都用于解决业务问题,而不是语言问题。当然,当你知道有所有这些更好的方法来做事情时,也会感到沮丧,但有时缺乏选择可以帮助集中注意力。:)

回复 作者 Alex Measday (未验证)

很棒的文章。我很高兴看到 COBOL 仍然活着并被大量使用。

我是一家仍然活着的主要大型机公司的 COBOL 编译器开发人员。

其中一家大型机公司?您的意思是 Amdahl 和 Hitachi 仍然在游戏中吗?
45-50 年前,它只是“白雪公主”和七个小矮人。
如果您能告诉我那可怜的七个小矮人的名字,可以获得额外的分数!

回复 作者 User

一个问题... 哪个版本的 COBOL 在 IF 语句中添加了 ELSE 子句?

我曾经在一个 DBA 团队中工作,该团队必须使用不支持 ELSE 子句的 COBOL 从大型机数据库中提取和转换数据以进行 Y2K。我们称之为“COBOL I”,但我找不到在线参考资料使用这个名称。

这太奇怪了!我不能说我听说过任何不支持 ELSE 的编程语言。如果您想做等效于 ELSE 的事情,您是否必须使用条件的否定重复 IF?您的 COBOL 是在大型机上还是在其他平台上运行的?

回复 作者 DocSalvager

Jean Sammet 将“强大的条件(即,IF THEN ELSE)结构”列为第一个版本的 COBOL,COBOL '60 的核心贡献之一。(请参阅 Jean Sammet 的“COBOL 的早期历史”,第 199-243 页,《编程语言史 I》(1978 年)。)我想知道您遇到的版本是否存在特定的实现问题。

回复 作者 DocSalvager

不,问题是第 73 列中的句点被忽略了。我第一次在 Stratus VOS 编译器上看到它,并且我能够在 GnuCOBOL 中复制它。它会发生在任何控制结构中。

回复 作者 Jeff B (未验证)

但是 GnuCOBOL 没有使用旧编译器,而是说“第 72 列之后的源代码文本” :-)

回复 作者 waltman

那是我的第一篇文章,我没有看到编辑它的方法。我的意思是,最后一个不支持 ELSE 子句的版本是什么?

我仍然使用 Cobol。我所有的业务程序都是用 Cobol 编写的。刚刚完成了一个新程序。GUI 界面,MySQL 数据库。从 IBM 360 开始,然后是 DEC10,1970 年代。然后是 TRS80 型号 II 和 III,最后是 PC。当前的服务器是 Linux Debian,带有 WIN10 用户机器。运行良好且速度快。

好文章。我在 80 年代的大学里学习了 COBOL,并在 90 年代早期用 COBOL 做了编程。我不能说我喜欢它,但它确实完成了工作。
您提到 COBOL“处理了估计 85% 的所有业务交易”。这个估计有具体的来源吗?
谢谢

很高兴在 cobol 代码中看到小写字母。

我几十年前就这样做了;没有其他人。很高兴发现代码编译得很好。受不了所有大写字母,因为它总是对你大喊大叫。呃。

我们的代码库都是小写的。也许他们那样做是因为我们在 Stratus 计算机上,而不是大型机上?我从没想过问任何人。

回复 作者 dwight (未验证)

我在 Cobol 中有很多编程工作。我也使用穿孔卡片作为编程工作的输入。最初,语句被键入并打孔,然后像其中一个博客中显示的那样由读卡器读取。我在 PDP/11 上完成了所有业务应用程序。
编写代码非常容易,甚至我的心都属于 Cobol。后来,我在中东为 IBM 计算机工作期间(1984-1994 年间)用 MF Cobol、RM Cobol、MS Cobol 编写了程序 10 年。我还用 AS/400 Cobol/400 编写了几个程序。我想即使在今天,我也会很乐意用 Cobol 编写代码。祝你好运。我在 Cobol 中做得很好

Creative Commons 许可本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.