2014-11-15 25 views
3

我正在处理一些旧的数据导入,并且遇到了一组来自外部来源的数据,这些数据用signed overpunch报告财务数字。我见过很多,但这是在我的时间之前。在我开始创建一个解析这些陌生人的函数之前,我想检查一下是否有标准的方法来处理这些陌生人。解析器用于签名overpunch值?

我想我的问题是,.Net框架提供了一个标准的工具来转换签名的overpunch字符串?如果不是.NET,是否有任何我可以使用的第三方工具,所以我不会重新发明轮子?

+0

在框架中没有内置的.Net解析器。 我们应该用自己的逻辑编写解析器。 [见本博客](http://www.ssistalk.com/2007/03/14/ssis-working-with-cobol-zoned-signed-decimals/)。 –

+0

什么是数据的原始来源(EBCDIC或ASCII)以及什么语言,如果EBCDIC –

+0

@VenkataPanga立即引用打包十进制作为分区 - 小数点后停止了我很快读取该垃圾。 –

回答

6

过冲数字(分区 - 十进制用Cobol)来自老穿孔卡片,他们过度冲出了一些最后的数字符号。该格式通常在Cobol中使用。

由于有两个Ascii码EBCDIC Cobol的编译器,有两个Ascii码EBCDIC版本的分区数字的。为了使它更加复杂,-0和+ 0值({}为美国EBCDIC(IBM037)中是说德国EBCDIC(IBM273,他们是AU)和不同又有所不同其他Ebcdic语言版本)。

成功处理,你需要知道:

  • 没有数据在EBCDIC或ASCII系统
  • 如果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 

对于美国EBCDIC征用这是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码划这是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。

例如

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 Translation包。

有几个商业软件包处理Cobol数据,他们往往是昂贵的。 有一些Java是一些可以处理Mainframe Cobol Data的开源软件包。

+0

谢谢,这让我开始了。嘿,我现在可以称自己是Cobol程序员吗?嘿嘿 – Slider345

+0

哇......你真的很喜欢以艰难的方式做事情。只是掩盖每个字节的最高半字节,并且该位置的值将为0-9。最后一个高点就是你的标志。适用于ASCII(x'30'-x'39')和EBCDIC(x'f0'-x'f9'),不需要考虑字符集。 –

+0

@JoeZitzelberger如果您在使用Ebcdic Zoned小数位字段并将其转换为Ascii或UTF8后可能无法工作,这可能就是这种情况。如果你看,我提供了一个纯粹的Ebcdic版本,我这样做 –

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; 
     } 
    } 

从我的头顶上做了这个,没有做过测试。

+1

这段代码使用US EBCDIC从ibm-Mainframe处理'Zoned Decimal'。它不适用于德语-EBCDIC或Ascii-Zoned(overpunch –

+1

我做了一些测试,使用'Regex'比@Andrew的答案慢得多。 – stack247

+0

但是同时,'Regex'方法也确保了输入的格式是正确的,这不是@Andrew的答案中发生的那样 – stack247

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); 
} 

请注意,这两种方法不验证字符串,并期待一个有效的数字。

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(); 
4

想必在规范文件或程序你被告知如何应对呢?没有?

正如布鲁斯马丁所说的,真正的Overpunch可以追溯到冲卡时代。你打了一个数字的最后一位数字,然后重新打孔(过度打印)卡上的相同位置。

包含在您的问题中的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'如果不是,则为X'D'。

分区十进制分隔符号:分隔符号包含字符'+' (如果数字为正数或0),如果不是' - '则为' - '。

内部十进制,无符号:低位字节 的低位4位包含X'F'。

内部十进制符号:低位字节 的低位4位包含X'C'(如果数目为正或0),并且X'D'包含X'D'(如果不是)。

COBOL算术语句生成的数据符合上述IBM 系统标准。但是,使用REDEFINES和组移动可能会更改数据以使其不再符合。如果使用NUMPROC(PFD),则 使用INITIALIZE语句初始化数据字段,而不是使用组移动的 。

使用NUMPROC(PFD)可能会影响数字数据的类测试。如果COBOL程序调用以PL/I或FORTRAN编写的程序 ,则应该使用NUMPROC(NOPFD)或NUMPROC(MIG)。

符号表示不仅受NUMPROC选项影响,还受安装时选项NUMCLS的影响 。

使用NUMPROC(MIG)来帮助将OS/VS COBOL程序迁移到 Enterprise 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'。如果正在使用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) 

如果数值数据是从外部转移系统,它应该始终以字符格式完成。它将使所有事情更容易编码,理解,维护和审计。

+0

这是一种会让我在晚上... j/k,感谢这个话题的深入探讨。 – Slider345

+0

@ Slider345如果他们不给你的角色数据,你只需要担心一切:-)祝你好运! –

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; 
} 
0

我只是想在这里吟诵,因为我写了一个类来处理这些。在我知道名称“签名过度”之前,我写了它,所以我称之为“打包签名”。我的方法的优点是它实际上是一个Java NumberFormatter,因此它很容易与任何使用java.lang.Number或java.text.NumberFormat的框架一起使用。 任何人在处理这些签名的overpunch数字方面有更多的经验,请随时打开拉请求,以使我的实现更加兼容不同的编码/变体等。 GitHub Repo