我正在处理一些旧数据导入,并从外部来源发现了大量数据,这些数据报告的财务数字为signed overpunch。我见过很多,但这还不错。在我创建一个解析这些陌生人的函数之前,我想检查是否有一种标准的方法来处理这些。
我想我的问题是,.Net框架是否提供了转换签名过冲字符串的标准工具?如果不是.NET,我可以使用任何第三方工具,所以我不会重新发明轮子吗?
答案 0 :(得分:7)
过度打孔数字(Cobol中的 Zoned-Decimal )来自旧打孔卡片,他们在数字的最后一位数字上打了一针。该格式通常用于Cobol。
由于有 Ascii 和 Ebcdic Cobol 编译器,因此有 Ascii 和 EBCDIC 版本划数字。为了使它更复杂,对于US-Ebcdic( IBM037 )的-0和+0值( {} 对于德语-Ebcdic来说是不同的( IBM273 äü)在其他Ebcdic语言版本中又有所不同。)
要成功处理,您需要知道:
如果数据在原始字符集中,您可以通过
计算符号对于EBCDIC,数字十六进制代码为:
Digit 0 1 2 .. 9
unsigned: x'F0' x'F1' x'F2' .. x'F9' 012 .. 9
Negative: x'D0' x'D1' x'D2' .. x'D9' }JK .. R
Positive: x'C0' x'C1' x'C2' .. x'C9' {AB .. I
对于 US-Ebcdic Zoned这是转换字符串的java代码:
int positiveDiff = 'A' - '1';
int negativeDiff = 'J' - '1';
lastChar = ret.substring(ret.length() - 1).toUpperCase().charAt(0);
switch (lastChar) {
case '}' : sign = "-";
case '{' :
lastChar = '0';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
lastChar = (char) (lastChar - positiveDiff);
break;
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
sign = "-";
lastChar = (char) (lastChar - negativeDiff);
default:
}
ret = sign + ret.substring(0, ret.length() - 1) + lastChar;
对于德语-EBCDIC {}成为äü,对于其他EBCDIC语言,您需要查找适当的编码页面。
对于 Ascii Zoned这是java代码
int positiveFjDiff = '@' - '0';
int negativeFjDiff = 'P' - '0';
lastChar = ret.substring(ret.length() - 1).toUpperCase().charAt(0);
switch (lastChar) {
case '@':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
lastChar = (char) (lastChar - positiveFjDiff);
break;
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
sign = "-";
lastChar = (char) (lastChar - negativeFjDiff);
default:
}
ret = sign + ret.substring(0, ret.length() - 1) + lastChar;
最后,如果你在EBCDIC工作,你可以像
一样计算它sign = '+'
if (last_digit & x'F0' == x'D0') {
sign = '-'
}
last_digit = last_digit | x'F0'
最后一个问题是小数点是未存储在Zoned中,假设为十进制。你需要看一下Cobol-Copybook。
e.g。
if the cobol Copybook is
03 fld pic s99999.
123 is stored as 0012C (EBCDIC source)
but if the copybook is (v stands for assumed decimal point)
03 fld pic s999v99.
then 123 is stored as 1230{
最好在Cobol中翻译!或使用Cobol翻译软件包。
有几个用于处理Cobol数据的商业软件包,它们往往很昂贵。 有些Java是一些可以处理Mainframe Cobol Data的开源软件包。
答案 1 :(得分:4)
据推测,在文件或程序的规范中,您会被告知如何处理此问题?否?
正如布鲁斯·马丁所说,真正的超越可以追溯到穿孔卡的日子。你打了一个数字的最后一位数字,然后重新打了一拳(过度打孔)卡上的相同位置。
您在问题中包含的Wiki链接就可以了。但我非常确定您的数据来源不是打孔卡。
虽然这个答案的一部分假设您使用的是大型机,但建议的解决方案与机器无关。
您的数据来源是大型机?我们不知道,虽然这是重要的信息。现在,让我们假设它是如此。
除非它是一个不变的非常旧的数据,否则它已在过去20年中在大型机上处理过。除非编译器使用(假设它来自COBOL程序)非常,非常旧,否则您需要知道编译器选项NUMPROC
的设置。原因如下:http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/igy3pg50/2.4.36?DT=20090820210412
默认为:NUMPROC(NOPFD)
缩写为:无
编译器接受任何有效的符号配置:X' A',X' B',X' C', X' D',X' E'或X' F'。 NUMPROC(NOPFD)是大多数人的推荐选择 例。
NUMPROC(PFD)提高了处理数字内部的性能 十进制和分区十进制数据。仅在您的程序中使用此选项 数据完全符合以下IBM系统标准:
分区十进制,无符号:符号字节的高位4位包含 X' F'
分区十进制,签名过冲:符号字节的高位4位 包含X' C'如果数字为正数或0,则X' D'如果不是。
分区十进制,单独符号:单独符号包含字符' +' 如果数字为正数或0,则' - '如果不是。
内部十进制,无符号:低位字节的低位4位 包含X' F'。
内部十进制,有符号:低位字节的低位4位 包含X' C'如果数字为正数或0,则X' D'如果不是。
COBOL算术语句生成的数据符合上述IBM 系统标准。但是,使用REDEFINES和组移动可以 更改数据,使其不再符合要求。如果你使用NUMPROC(PFD), 使用INITIALIZE语句初始化数据字段,而不是 使用小组移动。
使用NUMPROC(PFD)可能会影响数字数据的类测试。你应该 如果COBOL程序调用程序,请使用NUMPROC(NOPFD)或NUMPROC(MIG) 用PL / I或FORTRAN写的。
符号表示不仅受NUMPROC选项的影响,而且受到影响 也可以通过安装时选项NUMCLS。
使用NUMPROC(MIG)帮助迁移OS / VS COBOL程序 企业COBOL。当NUMPROC(MIG)生效时,以下内容 处理发生:
Preferred signs are created only on the output of MOVE statements and arithmetic operations. No explicit sign repair is done on input. Some implicit sign repair might occur during conversion. Numeric comparisons are performed by a decimal comparison, not a logical comparison.
这对你意味着什么?如果正在使用NUMPROC(NOPFD),您可能会看到X' A'通过X' F'在该字段的最后一个字节的高阶nybble中。如果正在使用NUMPROC(PFD),那么不应该看到X' C'或X' D'在那个位置。
请注意,如果您收到的文件是由已安装的Mainframe SORT产品生成的,则您可能会遇到相同的问题。
可能和不应该在规范中不是好事。
您的数据在金融环境中是否具有远程业务关键性?那你几乎肯定会有审计和合规问题。它的运行方式如下:
Auditor, "What do you do with the data when you receive it?"
You, "The first thing I do is change it"
Auditor, "Really? How do you verify the data once you have changed it?"
You, "Errr..."
你可能变得幸运,从未让审计师看过它。
所有这些非确定性词语对编程都不是很好。
那你怎么解决它?
您收到的有嵌入标志的数据应该没有字段。应该没有未表示为字符数据的数字字段(没有二进制,打包或浮点)。如果字段已签名,则应单独显示该标记。如果某个字段包含小数位,则应提供实际的.
或,
(取决于系统的所在国家/地区),或者作为替代字段使用缩放系数。
您的大型机人员难以做到吗?不是远程的。坚持下去。如果他们不这样做,请记录下来,以便产生的问题不是你的问题,而是问题。
如果呈现给您的所有数字数据都是普通字符数据(加号,减号,逗号,数字0到9),那么您在理解数据时绝对没有问题,无论是EBCDIC的任何变体还是ASCII的任何变体
请注意,来自COBOL的带小数位的任何字段都是精确的小数。 不要将其存储/使用在您的语言字段以外的任何可以处理精确十进制数量的字段中。
您不提供任何样本数据。所以这是一个样本:
123456{
这应该表示为:
+1234560
如果它有两位小数:
+12345.60
or
+12345602 (where the trailing 2 is a scaling-factor, which you validate)
如果要从外部系统传输数字数据,则应始终以字符格式进行。它将使编码,理解,维护和审计变得更加容易。
答案 2 :(得分:2)
分区十进制很容易,不需要操作字符。
private int ConvertOverpunch(byte[] number)
{
// Works for EBCDIC or ASCII, all charsets
int rtnVal = 0;
for(int i = 0; i<number.length; i++)
{
int digit = 0x0f & number[i];
rtnVal = (rtnVal * 10) + digit;
}
// Extract sign
// This works in EBCDIC
// Need to find out what your sign is in ASCII
if(0xD0 & number[number.length-1])
{
rtnVal *= -1;
}
return rtnVal;
}
答案 3 :(得分:1)
以下是另外两种方法,因此您有更多选择可供选择:
public static int Overpunch2Int_v1(string number)
{
number = number.ToLower();
char last = number.Last();
number = number.Substring(0, number.Length - 1);
if (last == '}' || (last >= 'j' && last <= 'r'))
{
number = "-" + number;
if (last == '}')
number += "0";
else
number += (char)(last - 'j' + '1');
}
else if (last == '{' || (last >= 'a' && last <= 'i'))
{
if (last == '{')
number += "0";
else
number += (char)(last - 'a' + '1');
}
return Int32.Parse(number);
}
public static int Overpunch2Int_v2(string number)
{
number = number.ToLower();
char last = number.Last();
number = number.Substring(0, number.Length - 1);
if (last >= '{')
number = (last == '}'? "-" : "") + number + "0";
else if (last >= 'a' && last <= 'r')
{
bool isNegative = last >= 'j';
char baseChar = isNegative ? 'j' : 'a';
number = (isNegative ? "-" : "") + number + (char)(last - baseChar + '1');
}
return Int32.Parse(number);
}
请注意,这两种方法都不会验证字符串并期望有效数字。
答案 4 :(得分:1)
如果你还没有足够的使用扩展方法的另一个选项,你可以通过使用其他帖子中的一些想法来改善这一点。
/// <summary>
/// Extension method to get overpunch value
/// </summary>
/// <param name="number">the text to convert</param>
/// <returns>int</returns>
public static int OverpunchValue(this String number)
{
int returnValue;
var ovpValue = OverPunchValues.Instance.OverPunchValueCollection.First(o => o.OverpunchCharacter ==
Convert.ToChar(number.Substring(number.Length - 1)));
returnValue = Convert.ToInt32(number.Substring(0, number.Length - 1) + ovpValue.NumericalValue.ToString());
return ovpValue.IsNegative ? returnValue * -1 : returnValue;
}
/*singleton to store values */
public class OverPunchValues
{
public List<OverPunchValue> OverPunchValueCollection { get; set; }
private OverPunchValues()
{
OverPunchValueCollection = new List<OverPunchValue>();
OverPunchValueCollection.Add(new OverPunchValue { OverpunchCharacter = '{', IsNegative = true, NumericalValue = 0 });
OverPunchValueCollection.Add(new OverPunchValue { OverpunchCharacter = 'J', IsNegative = true, NumericalValue = 1 });
//add the rest of the values here...
}
static readonly OverPunchValues _instance = new OverPunchValues();
public static OverPunchValues Instance
{
get { return _instance; }
}
}
public class OverPunchValue
{
public char OverpunchCharacter { get; set; }
public bool IsNegative { get; set; }
public int NumericalValue { get; set; }
public OverPunchValue()
{
}
}
然后你可以这样称呼它:
string str = "00345{";
int temp = str.OverpunchValue();
答案 5 :(得分:0)
private int ConvertOverpunch(string number)
{
number = number.ToLower();
Regex r = new Regex("}|j|k|l|m|n|o|p|q|r");
if(r.IsMatch(number))
{
number = "-" + number;
}
number = number.Replace('}', '0');
number = number.Replace('j', '1');
number = number.Replace('k', '2');
number = number.Replace('l', '3');
number = number.Replace('m', '4');
number = number.Replace('n', '5');
number = number.Replace('o', '6');
number = number.Replace('p', '7');
number = number.Replace('q', '8');
number = number.Replace('r', '9');
number = number.Replace('{', '0');
number = number.Replace('a', '1');
number = number.Replace('b', '2');
number = number.Replace('c', '3');
number = number.Replace('d', '4');
number = number.Replace('e', '5');
number = number.Replace('f', '6');
number = number.Replace('g', '7');
number = number.Replace('h', '8');
number = number.Replace('i', '9');
try
{
int intNumber = Convert.ToInt32(number);
return intNumber;
}
catch
{
return 0;
}
}
从头到尾做了这个,没有进行任何测试。
答案 6 :(得分:0)
我只想在这里写信,因为我写了一个类来处理这些问题。在我知道这个名字&#34; Signed Overpunch&#34;之前我写了它,所以我打电话给它#34;打包签名&#34;。我的方法的优点是它实际上是一个Java NumberFormatter,因此很容易使用任何使用java.lang.Number或java.text.NumberFormat的框架。 任何有处理这些签名超量号码的经验的人,请随时打开拉取请求,以使我的实现与不同的编码/变体等更兼容。 GitHub Repo