我发现我倾向于以太大的块来解决问题,因此我尝试从更小、更具体的问题开始。
我专门从事 PIC N 领域的工作,即国家领域。我需要确定我正在开发的程序的该字段中内容的长度和位置。
我所说的具体字段定义如下:
05 Bank-CODE PIC N(10).
可能有前导空格,也可能有尾随空格。在某些时候,我也需要测试它的嵌入式空间。
基本上,目标是能够在没有空格的情况下使用该字段的内容。
现在,我知道如何确定字段本身的长度:
05 BANK-CODE PIC N(10).
05 BK-LENGTH PIC S9(04) COMP.
MOVE LENGTH OF BANK-CODE TO BK-LENGTH
COMPUTE BK-LENGTH = FUNCTION LENGTH (BANK-CODE)
我只是不知道如何确定BANK-CODE中内容的长度和位置。
您正在寻找的是“修剪”,或者允许您修剪字段中的前导和尾随空格的信息。
COBOL 没有。除非,如前所述,您使用的是像 GnuCOBOL 这样的编译器,它确实可以。但我猜您正在使用 IBM 的 Enterprise COBOL。如果是,名称和特定版本将显示在编译器输出列表的每个页面的顶部。
仅仅因为 COBOL 没有修剪,并不能阻止您。这只是意味着您需要代码。
以下是一些来自“out There”(S8833TR.pdf 的搜索引擎,链接很长,很长的故事)的内容,并从客户情况中找到有关编码技巧的部分:
MOVE ' This is string 1 ' TO TEXT1
COMPUTE POS1 POS2 = 0
INSPECT TEXT1
TALLYING POS1
FOR LEADING SPACES
INSPECT FUNCTION REVERSE(TEXT1)
TALLYING POS2
FOR LEADING SPACES
MOVE TEXT1(POS1:LENGTH OF TEXT1 - POS2 - POS1)
TO TEXT2
Run Code Online (Sandbox Code Playgroud)
和
MOVE ' This is string 1 ' TO TEXT1
PERFORM VARYING POS1 FROM 1 BY 1
UNTIL TEXT1(POS1:1) NOT = SPACE
END-PERFORM
PERFORM VARYING POS2 FROM LENGTH OF TEXT1
BY -1 UNTIL TEXT1(POS2:1) NOT = SPACE
END-PERFORM
COMPUTE LEN = POS2 - POS1 + 1
MOVE TEXT1(POS1 : LEN) TO TEXT2 (1 : LEN)
Run Code Online (Sandbox Code Playgroud)
您将在文档中看到,正如您被告知的那样,存在一些性能影响FUNCTION REVERSE(第一个代码块比第二个代码块慢 31%),但要小心避免它。如果运行速度较慢,则更容易编码和理解。如果您的子程序将被大量使用,那么考虑性能是值得的。
该代码由 IBM 的 Tom Ross 提供。
Tom 的代码使用了“引用修改”。这是一种可以在编译时(如果起始位置和长度已知)定义字段的子字段的方法,或者如果起始位置和长度之一或两者都是可变的,则可以在运行时动态定义字段的子字段。引用修改是一种非常快速的数据访问方式。这也是一种非常懒惰和草率的访问数据的方式,它会混淆代码(那里可能会显示一些观点,但其他人可能不会持有这些观点)。
接下来的代码不使用引用修改,而是使用如下所示的内容:
01 the-text PIC X(30).
01 FILLER
REDEFINES the-text.
05 FILLER
OCCURS 30 TIMES.
10 the-byte-of-text PIC X.
Run Code Online (Sandbox Code Playgroud)
然后可以通过下标引用文本字节,而不是引用的组项(文本)。
对于引用修改和下标,POS1 可能会被定义为相同的。或者也可以。我会选择 PIC 99 BINARY(BINARY/COMP/COMP-4 在 Enterprise COBOL 中是相同的),但您的同事可能会坚持使用 PIC S9(4) COMP(旧习难改)。
现在,如果你想要更快:
PERFORM
VARYING POS1
FROM 1
BY 1
UNTIL ( NOT TEXT1-BYTE-IS-SPACE
( POS1 ) )
END-PERFORM
MOVE TEXT1 ( POS1 : ) TO TEXT2
Run Code Online (Sandbox Code Playgroud)
实际上 Tom 的代码中缺少一个部分,即 TEXT2 的初始化。
这段代码(我的)通过在引用修改中不使用“长度”来处理这个问题。为什么不?COBOL 字段是固定长度的(可变长度字段除外),因此您无法“删除”尾随空格。对于该示例,不必费心查找尾随空格,并使用最后的 MOVE 来确保上一次执行代码时 TEXT2 中不会留下任何数据(Tom 的示例中会有)。
请注意,我使用 REDEFINES 和数据名称作为下标 (POS1) 和 88 级条件名称。除了速度更快之外,您还可以使代码更清晰。在限制范围内,您可以同时进行这两项操作。
对于您的任务,您确实想知道尾随空格的长度:
示例的一个问题是它们通常不完整,例如缺少初始化。另一件不完整的事情是所有代码都假设至少存在一个非空白(因为使用文字作为源)。这通常是不现实的,而且对你的情况绝对没有用。
人们通常会通过在 PERFORM 的终止条件中包含“确保我没有跑完”来处理更一般的情况。然而,多个条件更难以理解,并且运行速度更慢。
IF field-in-use-is-blank
PERFORM no-field-to-deal-with
ELSE
PERFORM field-to-deal-with
END-IF
Run Code Online (Sandbox Code Playgroud)
所以立即做出两种不同的情况。在要处理的字段中,您可以知道至少有一个非空白字符。在无字段可处理的情况下,您知道整个字段都是空白的。
field-in-use-is-blank 是字段上的 88 级条件名称。
01 your-field PIC X(40).
88 field-in-use-is-blank VALUE SPACE.
Run Code Online (Sandbox Code Playgroud)
请注意,我使用的名称仅供说明之用。我总是根据您的实际目的建议有意义的名称。
PERFORM
VARYING POS2
FROM LENGTH OF TEXT1
BY -1
UNTIL ( TEXT1-SPACE-CHECK
( POS2 )
NOT EQUAL TO SPACE )
END-PERFORM
COMPUTE LEN = ( POS2
- POS1 )
+ 1
Run Code Online (Sandbox Code Playgroud)
从场地末端向后看,您不能使用 88 级。
现在你有了莱恩。我会对所有事物使用不同的名字,但这就是我。
以下是相关数据定义:
01 FILLER REDEFINES TEXT1.
05 FILLER OCCURS 50 TIMES.
10 TEXT1-SPACE-CHECK PIC X.
88 TEXT1-BYTE-IS-SPACE VALUE SPACE.
01 LEN COMP PIC S9(4).
01 POS1 COMP PIC S9(4).
01 POS2 COMP PIC S9(4).
Run Code Online (Sandbox Code Playgroud)
TEXT1 只是一个 50 字节的 PIC X 字段。剥鸡蛋皮的方法有很多种。上面的代码显示了 POS1 如何用于引用修改以及下标。LEN、POS1 和 POS2 的大小和类型是最佳猜测。它们都可以是二进制的(让我现代一下,因为它只是一个打字的东西......)PIC 99。在某些情况下,该定义将比原始定义更有效,否则相同。不要指望让你的任何前辈相信这一点。
COBOL 主要是一个“团队”的东西。按照您网站上的方式进行操作。如果本地“标准”很差或过时,您始终可以尝试更改它们,但是按照其他人的方式编码(技术)意味着团队更容易理解。显然,使用有意义的名称本身就是一种好处,这与技术无关。
以上适用于 PIC X(或 PIC A,但您不太可能看到这些)字段。PIC N 有什么不同?只是FUNCTION LENGTH代替LENGTH OF.
但是,PIC N 存在潜在的性能问题。如有必要,编译器会在后台将 National 转换为字母数字,然后再转换回来。为了性能,并且只有数字和简单的拉丁字母,我只会在最后一刻将所有内容转换为 PIC N。这可以只是一个简单的MOVE,编译器会为您生成代码。
忘记补充一点,您已经看到了“修剪器”的链接: https: //codereview.stackexchange.com/q/69220/21548