按字段长度排序
Sort on length of field
我想编写一个排序 JCL,要求对可变记录长度文件进行排序
输入文件:
Mark aaaaaaa
Amy bbbbbb
Paula ccccccccccc
按空格前的字段长度升序排序。那是根据第一个 col/word Mark、Amy 等的长度排序。根据它们的长度。
第二个就像按降序对空格后的字段进行排序,但如果字段中的任何元音应始终排在第一位,然后是其余数据。
接下来是第二部分,这里就像空格或 aaaaa、bbbbb 和 ccccc 之后的字段,我们需要按降序(按字母顺序)对其进行排序,但随后我们还需要检查该字段是否为 vovel,如果有 vovel 则该字段将始终处于顶部,因此预期的输出将类似于:
考虑到上面的输入文件输出文件将是:
标记 aaaaaaaa
宝拉 cccccc
艾米 bbbbbb
现在这里 vovel 作为第一个包含 aaaa 的记录在顶部,其余数据按降序排序。我想实现这个。
你问的根本不是一件简单的事情:-)
虽然 DFSORT 具有很多内在功能,但无法找到非 space 字符序列的长度。
所以你必须自己动手。
尽管固定长度记录(不同技术)也可以完成此任务,但可变长度记录更容易。
因为字段也是可变长度的,所以您需要 PARSE 来分隔字段。对于可变长度或可变位置的字段,PARSE 通常是答案。
PARSE 创建固定长度的解析字段,因此您必须知道文本的最大长度。在本例中,每个都选择了 30 个。
解决方案将逐步发展,因为您需要确保自己对它的理解。这些片段显示为 "stand alone" 代码,您可以 运行 看看会发生什么:
选项复制
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
IFTHEN=(WHEN=INIT,
BUILD=(1,4,%01,%02))
如果你运行那个,你会得到这个输出:
MARK AAAAAAA
AMY BBBBBB
PAULA CCCCCCCCCCC
INREC 运行s 在 SORT 之前,因此要在 SORT 之前对数据进行任何更改,请使用 INREC。 OUTREC 在 SORT 之后 运行s,OUTREC 在 OUTREC 之后。
现在,BUILD 只是为了显示 PARSEd 字段包含您想要的输出(不要担心大小写,如果您使用混合大小写,它就会像那样)。
WHEN=INIT 表示 "do this for each record, before the following IFTHEN statements (if any)"。你可以使用多个WHEN=INIT,你必须使用多个某种类型的IFTHEN来在多个阶段转换数据。
BUILD中的1,4是记录描述字(RDW),每条变长记录都有,在SORT中创建变长当前记录时总是需要的,但我们会用到它这里还有另一个目的。
下一阶段是 "extend" 记录,因为我们需要两个字段进行排序。对于可变长度记录,您扩展 "at the front"。总的来说:
BUILD=(1,4,extensionstuff,5)
这会生成当前记录的新版本,首先使用旧当前记录的 RDW,然后 "does some stuff" 创建扩展,然后从位置 5(变量的第一个数据字节)开始复制-length record) 到记录的末尾。
虽然RDW是"copied",但当时的RDW值是无关紧要的,因为它会被BUILD计算出来。它必须是一个 RDW 开始,除了实际的 RDW 之外,你不能把任何东西放在那里。
需要的另一个组件是扩展 SORT 键的记录。我们需要第一个字段的长度,我们需要一个 "flag" 来决定是否 "sort early" 用于包含元音的第二个字段。对于长度,有两个字节的二进制值会很方便。现在,我们只是为这些东西保留字节:
OPTION COPY
INREC BUILD=(1,4,2X,2X,X,5)
2X是2个空格,X是1个空格,所以一共是5个空格。它本可以写成 5X,在最终代码中最好是这样,但现在更清楚了。 运行 然后您将看到您的记录以五个空格为前缀。
有两个任务。第一个字段的长度,以及第二个字段是否包含元音。
第一个任务的关键是用 "nothing" 替换 PARSEd 字段中的空白。这将导致每替换一个空白,记录就会缩短一个。保存原始当前记录的长度,用当前记录的长度和定长(30)计算得出数据的长度。
第二个任务的关键应用了类似的技术。这次,更改第二个 PARSEd 字段,将 a、e、i、o、u 替换为 "nothing"。那么如果长度和原来一样,就没有元音。
FINDREP 看起来像这样:
IFTHEN=(WHEN=INIT,
FINDREP=(IN=C' ',
OUT=C'',
STARTPOS=n1,
ENDPOS=n2)),
您需要一个元音变体:
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',C'E',C'I',C'O',C'U'),
OUT=C'',
STARTPOS=n1,
ENDPOS=n2)),
至运行:
OPTION COPY
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
IFTHEN=(WHEN=INIT,
BUILD=(1,4,2X,X,%02)),
IFTHEN=(WHEN=INIT,
OVERLAY=(5:1,2)),
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',
C'E',
C'I',
C'O',
C'U'),
OUT=C'',
STARTPOS=8,
ENDPOS=38)),
IFTHEN=(WHEN=(1,4,BI,EQ,5,2,BI),
OVERLAY=(7:C'N'))
如果你 运行 那个,你会看到标志(第三个数据位置)现在是 space(对于存在的元音)或 "N"。不要担心所有 "A" 都消失了,它们仍然藏在 %02 中。
OVERLAY 可以对当前记录进行更改,而无需创建新的替换记录(BUILD 就是这样做的)。在创建新的当前记录长度后,您会看到下面使用 OVERLAY 来获取新的记录长度(BUILD 将从 RDW 获取原始记录长度)。
其他任务的类似过程。
我包含了一些额外的测试数据并对您的 SORT 顺序做了进一步的假设。这里是完整的,有注释的(注释可以保留,不影响处理),代码:
* PARSE CURRENT INPUT TO GET TWO FIELDS, HELD SEPARATELY FROM THE RECORD.
*
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
* MAKE A NEW CURRENT RECORD, RDW FROM EXISTING RECORD, THREE EXTENSIONS, AND
* A COPY OF THE FIRST PARSED FIELD.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
2X,
2X,
X,
%01)),
* STORE THE LENGTH OF THE NEW CURRENT RECORD ON THE CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(5:
1,2)),
* REPLACE BLANKS WITH "NOTHING" WITHIN THE COPY OF THE PARSED FIELD. THIS WILL
* AUTOMATICALLY ADJUST THE RDW ON THE CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
FINDREP=(IN=C' ',
OUT=C'',
STARTPOS=10,
ENDPOS=40)),
* CALCULATE THE LENGTH OF THE NON-BLANKS IN THE FIELD, BY SUBTRACTING PREVIOUS
* STORED RECORD-LENGTH FROM CURRENT RECORD-LENGTH (FIRST TWO BYTES, BINARY, OF
* RDW) AND ADDING 30 (LENGTH OF PARSED FIELD).
*
IFTHEN=(WHEN=INIT,
OVERLAY=(5:
1,2,BI,
SUB,
5,2,BI,
ADD,
+30,
TO=BI,
LENGTH=2)),
* MAKE A NEW CURRENT RECORD, COPYING RDW AND THE VALUE CALCULATED ABOVE, BLANKS
* (COULD BE COPIED) AND THEN THE SECOND PARSED FIELD.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
5,2,
2X,
X,
%02)),
* AGAIN SAVE THE LENGTH OF THE NEW CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(7:
1,2)),
* CHANGE ALL VOWELS TO "NOTHING". THIS WILL AUTOMATICALLY ADJUST THE RDW. FOR
* MIXED-CASE JUST EXTEND THE IN TO INCLUDE LOWER-CASE VOWELS AS WELL.
*
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',
C'E',
C'I',
C'O',
C'U'),
OUT=C'',
STARTPOS=10,
ENDPOS=40)),
* CALCULATE NUMBER OF VOWELS.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(7:
7,2,BI,
SUB,
1,2,BI,
TO=BI,
LENGTH=2)),
* MAKE A NEW CURRENT RECORD TO BE SORTED, WITH BOTH PARSED FIELDS.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
5,2,
7,2,
9,1,
%01,
%02)),
* SET THE FLAG TO "OUTSORT" THOSE RECORDS WITH A VOWEL IN THE SECOND FIELD.
*
IFTHEN=(WHEN=(7,2,BI,EQ,0),
OVERLAY=(9:
C'N'))
* SORT ON "OUTSORT FLAG", LENGTH OF NAME (DESCENDING), NAME, 2ND FIELD.
SORT FIELDS=(9,1,CH,A,
5,2,CH,D,
10,30,CH,A,
40,30,CH,A)
* FIELDS NEEDED TO BE IN FIXED POSITION FOR SORT, AND EXTENSION FIELDS NO
* LONGER NEEDED. ALSO REMOVE BLANKS FROM THE TWO FIELDS, KEEPING A SEPARATOR
* BETWEEN THEM. THIS COULD INSTEAD BE DONE ON THE OUTFIL.
*
OUTREC BUILD=(1,4,
10,60,
SQZ=(SHIFT=LEFT,
MID=C' '))
* CURRENTLY THE VARIABLE-LENGTH RECORDS ARE ALL THE SAME LENGTH (69 BYTES) SO
* REMOVE TRAILING BLANKS.
*
OUTFIL VLTRIM=C' '
广泛的测试数据:
MARK AAAAAAA
AMY BBBBBB
PAULA CCCCCCCCCCC
PAULA BDDDDDDDDDD
IK JJJJJJJJJJO
您还可以通过 "removing a line at a time" 从代码末尾查看代码的工作原理,这样您就可以看到转换是如何到达那个点的,或者通过 运行 使代码增加一行在代码开头的某个时间。
您和您的同事理解代码很重要。
有一些合理化的机会。如果你能解决这些问题,就意味着你理解了代码。应该是吧。
我想编写一个排序 JCL,要求对可变记录长度文件进行排序
输入文件:
Mark aaaaaaa
Amy bbbbbb
Paula ccccccccccc
按空格前的字段长度升序排序。那是根据第一个 col/word Mark、Amy 等的长度排序。根据它们的长度。
第二个就像按降序对空格后的字段进行排序,但如果字段中的任何元音应始终排在第一位,然后是其余数据。 接下来是第二部分,这里就像空格或 aaaaa、bbbbb 和 ccccc 之后的字段,我们需要按降序(按字母顺序)对其进行排序,但随后我们还需要检查该字段是否为 vovel,如果有 vovel 则该字段将始终处于顶部,因此预期的输出将类似于: 考虑到上面的输入文件输出文件将是:
标记 aaaaaaaa
宝拉 cccccc
艾米 bbbbbb
现在这里 vovel 作为第一个包含 aaaa 的记录在顶部,其余数据按降序排序。我想实现这个。
你问的根本不是一件简单的事情:-)
虽然 DFSORT 具有很多内在功能,但无法找到非 space 字符序列的长度。
所以你必须自己动手。
尽管固定长度记录(不同技术)也可以完成此任务,但可变长度记录更容易。
因为字段也是可变长度的,所以您需要 PARSE 来分隔字段。对于可变长度或可变位置的字段,PARSE 通常是答案。
PARSE 创建固定长度的解析字段,因此您必须知道文本的最大长度。在本例中,每个都选择了 30 个。
解决方案将逐步发展,因为您需要确保自己对它的理解。这些片段显示为 "stand alone" 代码,您可以 运行 看看会发生什么:
选项复制
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
IFTHEN=(WHEN=INIT,
BUILD=(1,4,%01,%02))
如果你运行那个,你会得到这个输出:
MARK AAAAAAA
AMY BBBBBB
PAULA CCCCCCCCCCC
INREC 运行s 在 SORT 之前,因此要在 SORT 之前对数据进行任何更改,请使用 INREC。 OUTREC 在 SORT 之后 运行s,OUTREC 在 OUTREC 之后。
现在,BUILD 只是为了显示 PARSEd 字段包含您想要的输出(不要担心大小写,如果您使用混合大小写,它就会像那样)。
WHEN=INIT 表示 "do this for each record, before the following IFTHEN statements (if any)"。你可以使用多个WHEN=INIT,你必须使用多个某种类型的IFTHEN来在多个阶段转换数据。
BUILD中的1,4是记录描述字(RDW),每条变长记录都有,在SORT中创建变长当前记录时总是需要的,但我们会用到它这里还有另一个目的。
下一阶段是 "extend" 记录,因为我们需要两个字段进行排序。对于可变长度记录,您扩展 "at the front"。总的来说:
BUILD=(1,4,extensionstuff,5)
这会生成当前记录的新版本,首先使用旧当前记录的 RDW,然后 "does some stuff" 创建扩展,然后从位置 5(变量的第一个数据字节)开始复制-length record) 到记录的末尾。
虽然RDW是"copied",但当时的RDW值是无关紧要的,因为它会被BUILD计算出来。它必须是一个 RDW 开始,除了实际的 RDW 之外,你不能把任何东西放在那里。
需要的另一个组件是扩展 SORT 键的记录。我们需要第一个字段的长度,我们需要一个 "flag" 来决定是否 "sort early" 用于包含元音的第二个字段。对于长度,有两个字节的二进制值会很方便。现在,我们只是为这些东西保留字节:
OPTION COPY
INREC BUILD=(1,4,2X,2X,X,5)
2X是2个空格,X是1个空格,所以一共是5个空格。它本可以写成 5X,在最终代码中最好是这样,但现在更清楚了。 运行 然后您将看到您的记录以五个空格为前缀。
有两个任务。第一个字段的长度,以及第二个字段是否包含元音。
第一个任务的关键是用 "nothing" 替换 PARSEd 字段中的空白。这将导致每替换一个空白,记录就会缩短一个。保存原始当前记录的长度,用当前记录的长度和定长(30)计算得出数据的长度。
第二个任务的关键应用了类似的技术。这次,更改第二个 PARSEd 字段,将 a、e、i、o、u 替换为 "nothing"。那么如果长度和原来一样,就没有元音。
FINDREP 看起来像这样:
IFTHEN=(WHEN=INIT,
FINDREP=(IN=C' ',
OUT=C'',
STARTPOS=n1,
ENDPOS=n2)),
您需要一个元音变体:
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',C'E',C'I',C'O',C'U'),
OUT=C'',
STARTPOS=n1,
ENDPOS=n2)),
至运行:
OPTION COPY
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
IFTHEN=(WHEN=INIT,
BUILD=(1,4,2X,X,%02)),
IFTHEN=(WHEN=INIT,
OVERLAY=(5:1,2)),
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',
C'E',
C'I',
C'O',
C'U'),
OUT=C'',
STARTPOS=8,
ENDPOS=38)),
IFTHEN=(WHEN=(1,4,BI,EQ,5,2,BI),
OVERLAY=(7:C'N'))
如果你 运行 那个,你会看到标志(第三个数据位置)现在是 space(对于存在的元音)或 "N"。不要担心所有 "A" 都消失了,它们仍然藏在 %02 中。
OVERLAY 可以对当前记录进行更改,而无需创建新的替换记录(BUILD 就是这样做的)。在创建新的当前记录长度后,您会看到下面使用 OVERLAY 来获取新的记录长度(BUILD 将从 RDW 获取原始记录长度)。
其他任务的类似过程。
我包含了一些额外的测试数据并对您的 SORT 顺序做了进一步的假设。这里是完整的,有注释的(注释可以保留,不影响处理),代码:
* PARSE CURRENT INPUT TO GET TWO FIELDS, HELD SEPARATELY FROM THE RECORD.
*
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
* MAKE A NEW CURRENT RECORD, RDW FROM EXISTING RECORD, THREE EXTENSIONS, AND
* A COPY OF THE FIRST PARSED FIELD.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
2X,
2X,
X,
%01)),
* STORE THE LENGTH OF THE NEW CURRENT RECORD ON THE CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(5:
1,2)),
* REPLACE BLANKS WITH "NOTHING" WITHIN THE COPY OF THE PARSED FIELD. THIS WILL
* AUTOMATICALLY ADJUST THE RDW ON THE CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
FINDREP=(IN=C' ',
OUT=C'',
STARTPOS=10,
ENDPOS=40)),
* CALCULATE THE LENGTH OF THE NON-BLANKS IN THE FIELD, BY SUBTRACTING PREVIOUS
* STORED RECORD-LENGTH FROM CURRENT RECORD-LENGTH (FIRST TWO BYTES, BINARY, OF
* RDW) AND ADDING 30 (LENGTH OF PARSED FIELD).
*
IFTHEN=(WHEN=INIT,
OVERLAY=(5:
1,2,BI,
SUB,
5,2,BI,
ADD,
+30,
TO=BI,
LENGTH=2)),
* MAKE A NEW CURRENT RECORD, COPYING RDW AND THE VALUE CALCULATED ABOVE, BLANKS
* (COULD BE COPIED) AND THEN THE SECOND PARSED FIELD.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
5,2,
2X,
X,
%02)),
* AGAIN SAVE THE LENGTH OF THE NEW CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(7:
1,2)),
* CHANGE ALL VOWELS TO "NOTHING". THIS WILL AUTOMATICALLY ADJUST THE RDW. FOR
* MIXED-CASE JUST EXTEND THE IN TO INCLUDE LOWER-CASE VOWELS AS WELL.
*
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',
C'E',
C'I',
C'O',
C'U'),
OUT=C'',
STARTPOS=10,
ENDPOS=40)),
* CALCULATE NUMBER OF VOWELS.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(7:
7,2,BI,
SUB,
1,2,BI,
TO=BI,
LENGTH=2)),
* MAKE A NEW CURRENT RECORD TO BE SORTED, WITH BOTH PARSED FIELDS.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
5,2,
7,2,
9,1,
%01,
%02)),
* SET THE FLAG TO "OUTSORT" THOSE RECORDS WITH A VOWEL IN THE SECOND FIELD.
*
IFTHEN=(WHEN=(7,2,BI,EQ,0),
OVERLAY=(9:
C'N'))
* SORT ON "OUTSORT FLAG", LENGTH OF NAME (DESCENDING), NAME, 2ND FIELD.
SORT FIELDS=(9,1,CH,A,
5,2,CH,D,
10,30,CH,A,
40,30,CH,A)
* FIELDS NEEDED TO BE IN FIXED POSITION FOR SORT, AND EXTENSION FIELDS NO
* LONGER NEEDED. ALSO REMOVE BLANKS FROM THE TWO FIELDS, KEEPING A SEPARATOR
* BETWEEN THEM. THIS COULD INSTEAD BE DONE ON THE OUTFIL.
*
OUTREC BUILD=(1,4,
10,60,
SQZ=(SHIFT=LEFT,
MID=C' '))
* CURRENTLY THE VARIABLE-LENGTH RECORDS ARE ALL THE SAME LENGTH (69 BYTES) SO
* REMOVE TRAILING BLANKS.
*
OUTFIL VLTRIM=C' '
广泛的测试数据:
MARK AAAAAAA
AMY BBBBBB
PAULA CCCCCCCCCCC
PAULA BDDDDDDDDDD
IK JJJJJJJJJJO
您还可以通过 "removing a line at a time" 从代码末尾查看代码的工作原理,这样您就可以看到转换是如何到达那个点的,或者通过 运行 使代码增加一行在代码开头的某个时间。
您和您的同事理解代码很重要。
有一些合理化的机会。如果你能解决这些问题,就意味着你理解了代码。应该是吧。