Cobol 字符串以尾部空格为分隔符

5
WORKING-STORAGE.
    FIRST-STRING    PIC X(15) VALUE SPACES.
    SECOND-STRING     PIC X(15) VALUE SPACES.
    OUTPUT-STRING     PIC X(31) VALUE SPACES.

如果 FIRST-NAME = 'JON SNOW, ' and LAST-NAME = 'KNOWS NOTHING. ',我该如何获取以下内容:
我想要获取:
OUTPUT-STRING = 'JON SNOW, KNOWS NOTHING.         '

当我尝试:

String FIRST-STRING DELIMITED BY SPACES
       ' ' DELIMITED BY SIZE
       SECOND-STRING DELIMITED BY SIZE
       INTO OUTPUT-STRING

我能看到:'琼恩什么都不知道。'

当我尝试:

String FIRST-STRING DELIMITED BY SIZE
       SECOND-STRING DELIMITED BY SIZE
       INTO OUTPUT-STRING

我得到了'JON SNOW,什么都不知道。'

我找到了一个小技巧,它由String FIRST-STRING DELIMITED BY ' '(两个空格)组成。但是不能保证我的FIRST-STRING不包含两个空格,这将导致丢失其中的一部分。


1
我已经更新了我的答案,如果你的FIRST-STRING可能为空,则非常重要。 - Bill Woodger
1
http://codereview.stackexchange.com/questions/69220/trim-functions-in-cobol - Kennah
1
@Kennah CodeReview 是针对已经按照预期工作的代码。这个不是。 - Kaz
@Zak 是的,我知道。我指向这个例子是因为我认为其中的讨论会有助于理解。 - Kennah
@Kennah 非常感谢你,我很感激你的帮助。 - raz_user
3个回答

11

首先,要赞赏一下你,因为许多人会用两个空格作为分隔符,并不会对可能的后果感到担忧。请注意,如果数据后跟一个尾随空格,则还会产生“意外”的输出。另请注意,OUTPUT-STRING的字段定义短了一个字节,因为您插入了一个空格来分隔数据。如果这两个字段都完全填满了数据,则将失去SECOND-STRING的最后一个字节。

COBOL是固定长度字段的语言(除非它们是可变的)。这意味着没有“标准”分隔符,因此任何字符或值都可以出现在字段的任何位置。此外,源字段比目标字段短时的默认填充字符是空格,这是单词的完全正常分隔符。

在您以及许多类似情况中,您需要知道字段实际数据部分的长度(不包括尾随空格)。

做到这一点的一种非常常见的方法是由@user4341206在他们的答案中提出的建议,https://dev59.com/sY3da4cB1Zd3GeqP6_Rd#31938039

在1985年的COBOL标准下,INSPECT可以用于计算前导空格,但不能用于计算尾随空格。可以先使用FUNCTION REVERSE将尾随空格转换为前导空格,以便INSPECT可以对其进行计数。

一旦您知道了尾随空格的数量,就可以使用LENGTH OF特殊寄存器或FUNCTION LENGTH来确定固定长度字段的长度(取决于编译器可能会评估)。字段长度与尾随空格数之间的差异给出了数据的长度。

一旦您知道了数据的长度,并牢记它可能为空(取决于数据可能性)并且长度可能与字段相同

请注意,如果有大量数据,您可能不想反转字段并使用INSPECT(与从字段末尾循环以计算尾随空格相比,这可能是运行时例程)。

请注意,像AcuCOBOL(现在是Micro Focus的COBOL产品的一部分)这样的编译器具有一种语言扩展,可为INSPECT提供TRAILING选项。请注意,即使是2014年的COBOL标准也没有TRAILING选项作为INSPECT的选项。

无论哪种方式,有了数据的长度,您就完成了。在某种程度上。

您可以在STRING语句中使用引用修改:

String FIRST-STRING ( 1 : length-field-you-define ) DELIMITED BY SIZE
       ' ' DELIMITED BY SIZE
       SECOND-STRING DELIMITED BY SIZE
   INTO OUTPUT-STRING

请注意,您应该能够删除“BY SIZE”,因为“SIZE”是默认值,但这确实使人类读者更加清晰明了。

您还可以在目标字段上使用带有引用修改的MOVE语句:

MOVE FIRST-STRING            TO OUTPUT-STRING  
                                 ( 1 : length-field-you-define )
MOVE SPACE                   TO OUTPUT-STRING  
                                 ( length-field-you-define + 1 : 1 )
MOVE SECOND-STRING           TO OUTPUT-STRING  
                                 ( length-field-you-define + 2 :  )

在参考修改中存在一个具体的问题(在其他答案中提到),即您的长度字段不应为零。

长度的计算结果应该是一个正的非零整数。

在这个上下文中,长度是参考修改符号中冒号后的第二项。在这种情况下,它意味着您定义的长度字段不得为零,如果FIRST-STRING完全为空格,则可以计算为零。

潜在的问题在于:

MOVE FIRST-STRING            TO OUTPUT-STRING  
                                 ( 1 : length-field-you-define )
因此,根据您的数据(如果可能包含空格),您必须“保护”它们。
    IF FIRST-STRING EQUAL TO SPACE
        PERFORM                  COPY-SECOND-STRING-ONLY
    ELSE
        PERFORM                  CONCATENATE-FIRST-AND-SECOND
    END-IF
    ...
COPY-SECOND-STRING-ONLY.
    MOVE SECOND-STRING           TO OUTPUT-STRING
    .
CONCATENATE-FIRST-AND-SECOND.
    calculate length
    MOVE FIRST-STRING            TO OUTPUT-STRING  
                                    ( 1 : length-field-you-define )
    MOVE SPACE                   TO OUTPUT-STRING  
                                    ( length-field-you-define + 1 : 1 )
    MOVE SECOND-STRING           TO OUTPUT-STRING  
                                    ( length-field-you-define + 2 :  )
    .

如果使用长度为零的引用修改器,则结果未定义,尽管它可能会在您的编译器中“工作”。

对于字符串和可变长度字段的解决方案不会“失败”,因为除了引用修改器之外的编译器都可以接受长度为零的项。

然而,出于两个原因应该采用同样的“保护”措施:首先要插入一个前导空格(“分隔符”);其次,要使代码明确,这样人们就不必自问“第一个字段为空时会发生什么”;还可以节省处理时间。这样也可以更好地“描述数据”。与准确的程序设计所需的“了解数据”一样,您的程序描述数据越多,就越难出现遗漏或差错,并且更易于理解和更易于在数据结构更改时进行更改。

您还可以查看使用 WITH POINTER 选项的 STRING。首先,将 FIRST-STRING 移动到 OUTPUT-STRING(这也会将 OUTPUT-STRING 中未使用的字节清除为空格)。然后,在您定义的长度字段中加一(表示介于两者之间的空格),并在带有 POINTER 的 STRING 中使用它。

虽然这是完全有效的,但如果使用它,应该加上注释,因为许多经常使用 STRING 的人对 WITH POINTER 的使用没有概念,所以需要帮助他们。

另一种可能性是使用可变长度字段。

不幸的是,并非所有 COBOL 编译器都能轻松实现这一点。这将需要一个“复杂 ODO”,但这是 IBM 对该语言的扩展,而不是标准。

LINKAGE SECTION.
01  L-MAPPING-OF-OUTPUT-STRING.
    05  L-MOOS-FIRST-STRING.
        10  FILLER OCCURS 0 TO 15 TIMES
            DEPENDING ON length-field-you-define.
            15  FILLER                          PIC X.
    05  L-MOOS-SEPARATOR-SPACE                  PIC X.
    05  L-MOOS-SECOND-STRING                    PIC X(15).

    ...
    SET ADDRESS OF L-MAPPING-OF-OUTPUT-STRING
                             TO ADDRESS OF 
                                 OUTPUT-STRING  
    MOVE FIRST-STRING        TO L-MOOS-FIRST-STRING
    MOVE SPACE               TO L-MOOS-SEPARATOR-SPACE
    MOVE SECOND-STRING       TO L-MOOS-SECOND-STRING

如果您有大量数据,最快的方式是参考-修改-仅建议。我认为参考-修改倾向于使代码混淆,因为人们倾向于以一种混淆的(且不必要的)方式使用它。

我的首选是最后一种方法,其中PROCEDURE DIVISION代码非常简单:您可以在第一个字段中找到数据的长度;只需进行三个普通的MOVE操作即可。

也许您可以尝试每一种方法,以便更加了解未来情况的可能性。


没有人曾经给过我如此完整的答案,非常感谢。 - raz_user
1
您之前没有问过 COBOL 的问题 :-) - Bill Woodger
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - raz_user

8

我不知道这是否会对您有所帮助,但如果您想删除第一个字符串的尾随空格,您可以在连接字符串之前执行以下操作:

INSPECT FUNCTION REVERSE(FIRST-STRING) TALLYING W-SPACES FOR LEADING SPACES

COMPUTE W-FIRST-STRING-LEN = LENGTH OF FIRST-STRING - W-SPACES

FIRST-STRING(1:W-FIRST-STRING-LEN)现在包含了没有尾随空格的第一个字符串(JOHN SNOW,)。


2
谢谢。下一件事是再次保护FIRST-STRING完全为空。引用修饰符的长度部分不能为零。 - Bill Woodger
谢谢,我点赞了你的回答因为它提供了帮助,但是我接受了@BillWoodger的答案,因为它是我的问题的终极答案。 - raz_user

0
我更喜欢使用内联执行来查找数据的长度,去除尾随空格。最小长度为1将允许字符串命令成功,即使数据全部为空格也可以。
PERFORM VARYING FIELD-LEN
   FROM LENGTH OF SEARCH-FIELD BY -1
  UNTIL FIELD-LEN = 1
     OR SEARCH-FIELD(FIELD-LEN:1) NOT = SPACE
END-PERFORM.

STRING SEARCH-FIELD(1:FIELD-LEN) DELIMITED BY SIZE
       etc...

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