Cobol 字符串由尾随空格分隔

Cobol String Delimited By Trailing SPACES

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

我得到'JON KNOWS NOTHING. '

当我尝试时:

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

我得到'JON SNOW, KNOWS NOTHING. '

我发现了一个由 String FIRST-STRING DELIMITED BY ' '(两个空格)组成的调整 但是不能保证我的 FIRST-STRING 不包含两个空格,这会导致丢失它的一部分。

我不知道这是否对你有帮助,但如果你想删除第一个字符串的尾随空格,你可以在连接字符串之前做这样的事情:

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,)

首先,荣誉,因为许多人会使用由两个分隔的 space 并且根本不关心可能的后果。请注意,如果数据后跟一个尾随 space,您还会得到 "unexpected" 输出。另请注意,您的 OUTPUT-STRING 字段定义是一个字节短,因为您要插入 space 来分隔数据。由于两个字段都完全填满了数据,您将丢失 SECOND-STRING 的最后一个字节。

COBOL 是一种固定长度字段的语言(除非它们是可变的)。这意味着没有 "standard" 分隔符,因此任何字符或值都可以出现在字段中的任何位置。此外,源字段短于目标字段的默认填充字符是 space,这是一个完全正常的单词分隔符。

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

@user4341206 在他们的回答中建议的一种非常常见的方法,

在 1985 COBOL 标准下,INSPECT 可用于计算 leading spaces,但不能用于计算 trailing space秒。可以先使用 FUNCTION REVERSE 将尾随 space 变成前导 space,以便 INSPECT 可以计算它们。

一旦知道尾随空白的数量,就可以使用 LENGTH OF 特殊寄存器或 FUNCTION LENGTH 来确定固定长度字段的长度(两者都是(或者可以是,取决于编译器)在编译时评估)。字段长度与尾随空格数之间的差异就是数据的长度。

一旦你有了数据的长度,请记住它可能是空白的(取决于数据的可能性)并且它可能与字段的长度相同

请注意,如果您有大量数据,与从字段末尾开始的简单循环相比,您可能不希望反转字段并使用 INSPECT(可能是 运行-time 例程)计算尾随空格。

请注意,像 AcuCOBOL(现在是 Micro Focus 的 COBOL 产品的一部分)这样的编译器有一个语言扩展,它提供 TRAILING 作为 INSPECT 的一个选项。请注意,即使是 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 是默认值,但它确实让人类更清楚 reader。

您还可以在目标字段上使用带有引用修改的 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 :  )

引用修改(在另一个答案中提到)存在一个特定问题,即您的长度字段不应为零。

The evaluation of length shall result in a positive nonzero integer.

在此上下文中的长度是引用修改表示法中 : 之后的第二项。在这种情况下,这意味着您定义的长度字段不能为零,如果 FIRST-STRING 完全为 space.

,则可以计算为零

潜在的问题在于:

MOVE FIRST-STRING            TO OUTPUT-STRING  
                                 ( 1 : length-field-you-define )

因此,根据您的数据(如果它可能包含空白),您必须 "protect" 反对。

    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 :  )
    .

如果您使用长度为零的引用修改,结果是未定义的,尽管它可能 "work" 您的编译器。

具有 STRING 和可变长度字段的解决方案不会 "fail",因为引用修改之外的编译器对零长度项目很满意。

但是,出于两个原因,应该使用相同的 "protection":您将插入一个前导空格("separator");您将使您的代码明确,这样人们就不必问自己 "what happens when the first field is blank";您将节省处理费用。

这样你的程序也"describes your data"更好。加上"know your data"作为精确程序设计的必要条件,你的程序对数据的描述越多,越不容易产生错漏错,越容易理解,也越容易当数据结构发生变化时发生变化。

您还可以使用 WITH POINTER 选项查看 STRING。首先,将 FIRST-STRING 移动到 OUTPUT-STRING(这也会将 OUTPUT-STRING 中未使用的字节清除到 space)。然后将一个添加到您定义的长度字段(用于中间 space)并在 WITH POINTER 的 STRING 中使用它。

虽然这完全正确,但如果使用,这是一个评论的机会,因为很多经常使用 STRING 的人不知道 WITH POINTER 的用法,所以帮助他们。

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

不幸的是,并不是所有的 COBOL 编译器都能让这变得简单。 "Complex 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。

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

我更喜欢内联函数来查找数据的长度,去除尾随空格。最小长度为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...