Как я могу упаковать десятичный и обычный текст в одном файле?
Мне нужно создать файл фиксированной ширины с несколькими столбцами в упакованном десятичном формате и несколькими столбцами в обычном числовом формате. Я был в состоянии генерировать. Я сжал файл и передал его команде мэйнфреймов. Они импортировали его, распаковали файл и конвертировали в EBCDIC. Они смогли без проблем получить упакованные десятичные столбцы, но обычные числовые поля, похоже, испортились и не читаются. Есть ли что-то конкретное, что мне нужно сделать во время обработки / архивирования моего файла перед отправкой на мэйнфрейм? Я использую десятичную упаковку COMP3. В настоящее время работает на Windows XP, но реальное производство будет на RHEL.
Заранее спасибо за помощь. Это срочно.
Отредактировано 06 июня 2011 года:
Вот как это выглядит, когда я включаю HEX.
. . . . . . . . . . A . .
333333333326004444
210003166750C0000
Буква "А" в первом ряду имеет небольшой акцент, поэтому это не фактический верхний регистр А.
210003166 - это сырое десятичное число. Значение упакованного десятичного числа перед преобразованием comp3 равно 000000002765000 (при необходимости мы можем игнорировать начальные нули).
ОБНОВЛЕНИЕ 2: 7 июня 2011 г. Вот как я конвертирую создание файла, который загружается в мэйнфрейм: Файл содержит два столбца - Идентификационный номер и количество. Идентификационный номер не требует преобразования comp3, а сумма требует преобразования comp3. Comp3 преобразование выполняется в конце Oracle. Вот запрос для выполнения преобразования:
Select nvl(IDENTIFIER,' ') as IDENTIFIER, nvl(utl_raw.cast_to_varchar2(comp3.convert(to_number(AMOUNT))),'0') as AMOUNT from TABLEX where IDENTIFIER = 123456789
После выполнения запроса я делаю следующее в Java:
String query = "Select nvl(IDENTIFIER,' ') as IDENTIFIER, nvl(utl_raw.cast_to_varchar2(comp3.convert(to_number(AMOUNT))),'0') as AMOUNT from TABLEX where IDENTIFIER = 210003166"; // this is the select query with COMP3 conversion
ResultSet rs = getConnection().createStatement().executeQuery(sb.toString());
sb.delete(0, sb.length()-1);
StringBuffer appendedValue = new StringBuffer (200000);
while(rs.next()){
appendedValue.append(rs.getString("IDENTIFIER"))
.append(rs.getString("AMOUNT"));
}
File toWriteFile = new File("C:/transformedFile.txt");
FileWriter writer = new FileWriter(toWriteFile, true);
writer.write(appendedValue.toString());
//writer.write(System.getProperty(ComponentConstants.LINE_SEPERATOR));
writer.flush();
appendedValue.delete(0, appendedValue.length() -1);
Сгенерированный таким образом текстовый файл вручную заархивируется с помощью инструмента winzip и передается команде мэйнфреймов. Команда мэйнфреймов загружает файл в мэйнфрейм и просматривает файл с помощью HEXON.
Теперь, перейдя к преобразованию старших четырех битов зонированной десятичной дроби, я должен сделать это прежде, чем исправить это в файл? Или я должен применить переворачивание в конце мэйнфрейма? На данный момент, я сделал переворачивание в конце Java с помощью следующего кода:
public static String toZoned(String num) {
if (num == null) {
return "";
}
String ret = num.trim();
if (num.equals("") || num.equals("-") || num.equals("+")) {
// throw ...
return "";
}
char lastChar = ret.substring(ret.length() - 1).charAt(0);
//System.out.print(ret + " Char - " + lastChar);
if (lastChar < '0' || lastChar > '9') {
} else if (num.startsWith("-")) {
if (lastChar == '0') {
lastChar = '}';
} else {
lastChar = (char) (lastChar + negativeDiff);
}
ret = ret.substring(1, ret.length() - 1) + lastChar;
} else {
if (num.startsWith("+")) {
ret = ret.substring(1);
}
if (lastChar == '0') {
lastChar = '{';
} else {
lastChar = (char) (lastChar + positiveDiff);
}
ret = ret.substring(0, ret.length() - 1) + lastChar;
}
//System.out.print(" - " + lastChar);
//System.out.println(" -> " + ret);
return ret;
}
Идентификатор становится 21000316F в конце Java, и это то, что записывается в файл. Я передал файл команде мэйнфреймов и ожидаю вывода с HEXON. Дай мне знать, если я что-то упустил. Благодарю.
ОБНОВЛЕНИЕ 3: 9 июня 2011
Хорошо, у меня есть результаты мэйнфреймов. Я делаю это сейчас.
public static void main(String[] args) throws FileNotFoundException {
// TODO Auto-generated method stub
String myString = new String("210003166");
byte[] num1 = new byte[16];
try {
PackDec.stringToPack("000000002765000",num1,0,15);
System.out.println("array size: " + num1.length);
} catch (DecimalOverflowException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (DataException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
byte[] ebc = null;
try {
ebc = myString.getBytes("Cp037");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
PrintWriter pw = new PrintWriter("C:/transformationTextV1.txt");
pw.printf("%x%x%x%x%x%x%x%x%x",ebc[0],ebc[1],ebc[2],ebc[3],ebc[4], ebc[5], ebc[6], ebc[7], ebc[8]);
pw.printf("%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x",num1[0],num1[1],num1[2],num1[3],num1[4], num1[5], num1[6], num1[7],num1[8], num1[9],num1[10], num1[11],num1[12], num1[13], num1[14],num1[15]);
pw.close();
}
И я получаю следующий вывод:
Á.Á.Á.Á.Á.Á.Á.Á.Á.................Ä
63636363636363636333333333333333336444444444444444444444444444444444444444444444
62616060606361666600000000000276503000000000000000000000000000000000000000000000
Я должен делать что-то очень неправильно!
ОБНОВЛЕНИЕ 4: 14 июня 2011
Этот запрос был решен после использования предложения Джеймса. В настоящее время я использую приведенный ниже код, и он дает мне ожидаемый результат:
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String myString = new String("210003166");
byte[] num1 = new byte[16];
try {
PackDec.stringToPack("02765000",num1,0,8);
} catch (DecimalOverflowException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (DataException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
byte[] ebc = null;
try {
ebc = myString.getBytes("Cp037");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileOutputStream writer = new FileOutputStream("C:/transformedFileV3.txt");
writer.write(ebc,0,9);
writer.write(num1,0,8);
writer.close();
}
4 ответа
Поскольку вы кодируете на Java и вам требуется сочетание EBCDIC и COMP-3 в выходных данных, вам нужно будет выполнить преобразование Unicode в EBCDIC в вашей собственной программе.
Вы не можете оставить это на усмотрение утилиты передачи файлов, так как это повредит ваши поля COMP-3.
Но, к счастью, вы используете Java, поэтому с помощью метода getBytes класса string это легко.
Рабочий пример:
package com.tight.tran;
import java.io.*;
import name.benjaminjwhite.zdecimal.DataException;
import name.benjaminjwhite.zdecimal.DecimalOverflowException;
import name.benjaminjwhite.zdecimal.PackDec;
public class worong {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String myString = new String("210003166");
byte[] num1 = new byte[16];
try {
PackDec.stringToPack("000000002765000",num1,0,15);
System.out.println("array size: " + num1.length);
} catch (DecimalOverflowException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (DataException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
byte[] ebc = null;
try {
ebc = myString.getBytes("Cp037");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileOutputStream writer = new FileOutputStream("C:/transformedFile.txt");
writer.write(ebc,0,9);
writer.write(num1,0,15);
writer.close();
}
}
Производит (для меня!):
0000000: f2f1 f0f0 f0f3 f1f6 f600 0000 0000 0000 ................
0000010: 0000 0000 2765 000c 0d0a ....'e....
"... преобразовано в EBCDIC..." может быть частью проблемы.
Если процесс преобразования мэйнфрейма не "знает" о макете записи, с которым он работает (то есть, какие столбцы содержат двоичные, упакованные и / или символьные данные), он может что-то испортить, потому что процесс отображения зависит от формата.
Вы указали, что с данными COMP-3 все в порядке, я готов поспорить, что либо "преобразованный в EBCDIC" ничего не делает, либо он выполняет какое-то преобразование ASCII в COMP-3 для всех ваших данных - таким образом, испортить не-COMP-3 данных.
Как только вы доберетесь до мэйнфрейма, вы должны увидеть следующее:
COMP-3 - каждый байт содержит 2 цифры, кроме последней (наиболее правая, наименее значимая). Младший значащий байт содержит только 1 десятичную цифру в старших 4 битах и поле знака в младших 4 битах. Каждая десятичная цифра записывается в шестнадцатеричном формате (например, 5 = B'0101')
Десятичная зона (нормальные числа) - каждый байт содержит 1 десятичную цифру. Старшие четыре бита должны всегда содержать HEX F, за исключением, возможно, самого младшего старшего байта, где старшие 4 бита могут содержать знак, а младшие 4 бита - цифру. 4-разрядная цифра записывается в шестнадцатеричном формате (например, 5 = B'0101')
Вам нужно посмотреть, как выглядят преобразованные данные в архиве на мэйнфрейме. Попросите кого-нибудь "ПРОСМОТРЕТЬ" ваш файл на мэйнфрейме с помощью "HEX ON", чтобы вы могли увидеть, каково фактическое HEX-содержимое вашего файла. Оттуда вы сможете выяснить, через какие обручи и петли вам нужно прыгать, чтобы сделать эту работу.
Вот несколько ссылок, которые могут быть вам полезны:
Обновление: если ребята из мэйнфрейма видят правильные цифры при просмотре с помощью "HEX ON", то есть две возможные проблемы:
- Цифра хранится не в том клеве. Цифра должна быть видна в нижних 4 битах. Если он находится в старших 4 битах, это определенно проблема.
- Полубит без цифры (старшие 4 бита) не содержит HEX 'F' или действительного значения знака. Цифры без знака всегда содержат HEX 'F' в старших 4 битах байта. Если число подписано (например, PIC S9(4) - или что-то в этом роде), старшие 4 бита самой младшей значащей цифры (последней) должны содержать HEX "C" или "D".
Вот немного скриншота того, как должен выглядеть BROWSE с HEX ON:
File Edit Edit_Settings Menu Utilities Compilers Test Help
VIEW USERID.TEST.DATA - 01.99 Columns 00001 00072
Command ===> Scroll ===> CSR
****** ***************************** Top of Data ******************************
000001 0123456789
FFFFFFFFFF44444444444444444444444444444444444444444444444444444444444444
012345678900000000000000000000000000000000000000000000000000000000000000
------------------------------------------------------------------------------
000002 |¬?"±°
012345678944444444444444444444444444444444444444444444444444444444444444
FFFFFFFFF000000000000000000000000000000000000000000000000000000000000000
------------------------------------------------------------------------------
000003 àíÃÏhr
012345678944444444444444444444444444444444444444444444444444444444444444
012345678900000000000000000000000000000000000000000000000000000000000000
------------------------------------------------------------------------------
Строки, начинающиеся с "000001", "000002" и "000003", показывают "простой" текст. две строки под каждой из них показывают шестнадцатеричное представление символа над ним. Первая строка HEX показывает 4 старших бита, вторая строка 4 младших бита.
- В строке 1 содержится номер "0123456789", за которым следуют пробелы (HEX 40).
- Строка 2 показывает мусор, потому что верхний и нижний кусочки перевернуты. Точный глупый символ - просто вопрос выбора кодовой страницы, поэтому не увлекайтесь тем, что видите.
- В строке 3 показан аналогичный мусор, поскольку верхний и нижний полубайты содержат цифры.
Строка '000001' - это то, что вы должны увидеть для беззнаковых зонированных десятичных чисел на мэйнфрейме IBM, используя EBCDIC (однобайтовый набор символов).
ОБНОВЛЕНИЕ 2
Вы добавили HEX-дисплей к своему вопросу 6 июня. Я думаю, возможно, было несколько проблем с форматированием. Если это то, что вы пытались показать, вам может помочь следующее обсуждение:
..........A..
33333333326004444
210003166750C0000
Вы отметили, что это отображение двух "чисел":
- 210003166 в десятичной зоне
- 000000002765000 в КОМП-3
Вот что ожидает мэйнфрейм IBM:
210003166 :Á : <-- Display character
FFFFFFFFF00002600 <-- Upper 4 bits of each byte
2100031660000750C <-- Lower 4 bits of each byte
Обратите внимание на различия между тем, что у вас есть, и выше:
- Верхние 4 бита данных Zoned Decimal на вашем дисплее содержат HEX '3', они должны содержать HEx 'F'. Нижние 4 бита содержат ожидаемую цифру. Исправьте эти старшие 4 бита, и вы должны быть готовы. Кстати... мне кажется, что любое "преобразование", которое вы предприняли в Zoned Decimal, не имеет никакого эффекта. Битовые комбинации, которые у вас есть для каждой цифры в зонированном десятичном числе, соответствуют цифрам в наборе символов ASCII.
- В поле COMP-3 вы указали, что начальные нули могут быть обрезаны. Извините, но они либо являются частью числа, либо нет! Мой дисплей выше содержит начальные нули. На вашем дисплее появляются усеченные начальные нули, а затем заполненные конечные байты пробелами (HEX 40). Это не сработает! Поля COMP-3 определены с фиксированным количеством цифр, и все цифры должны быть представлены - это означает, что для заполнения старших цифр каждого числа требуются начальные нули.
Исправление Zoned Decimal должно быть довольно простым... Исправление COMP-3, вероятно, состоит просто в том, чтобы не удалять начальные нули (в противном случае это выглядит довольно хорошо).
ОБНОВЛЕНИЕ 3...
Как вы переворачиваете 4 старших бита? У меня сложилось впечатление, что вы можете делать свое преобразование с помощью Java-программы. Я, к сожалению, программист на языке COBOL, но я попробую (не смейтесь)...
Исходя из того, что я видел здесь, все, что вам нужно сделать, это взять каждую цифру ASCII и перевернуть старшие 4 бита в HEX F, и в результате будет получена эквивалентная цифра без знака Zoned Decimal EBCDIC. Попробуйте что-то вроде...
public static byte AsciiToZonedDecimal(byte b) {
//flip upper 4 bits to Hex F...
return (byte)(b | 0xF0)
};
Примените вышеупомянутое к каждой цифре ASCII, и результат должен быть беззнаковым десятичным десятичным числом EBCDIC.
ОБНОВЛЕНИЕ 4...
На этом этапе ответы Джеймса Андерсона должны направить вас в нужное русло.
Джеймс указал вам name.benjaminjwhite.zdecimal, и похоже, что в нем есть все классы Java, необходимые для преобразования ваших данных. Метод StringToZone должен иметь возможность преобразовывать строку IDENTIFIER, которую вы возвращаете из Oracle, в байтовый массив, который вы затем добавляете в выходной файл.
Я не очень знаком с Java, но я считаю, что строки Java хранятся внутри как Unicode-символы длиной 16 бит. Символы EBCDIC, которые вы пытаетесь создать, имеют длину всего 8 бит. Учитывая это, вам может быть лучше записать в выходной файл, используя байтовые массивы (в отличие от строк). Просто догадка от не Java-программиста.
toZoned
Метод в вашем вопросе выше, кажется, касается только первых и последних символов строки. Частично проблема заключается в том, что необходимо преобразовать каждый символ - 4 старших бита каждого байта, за исключением, возможно, последнего, должны быть исправлены, чтобы содержать шестнадцатеричный код F. Нижние 4 бита содержат одну цифру.
Кстати... Вы можете подобрать исходный код для этого служебного класса Java по адресу: http://www.benjaminjwhite.name/zdecimal/
"Они смогли получить упакованные десятичные столбцы без каких-либо проблем, но обычные числовые поля, похоже, испортились", похоже, указывают на то, что они не переводили ASCII в EBCDIC.
Ноль ASCII x'30'должен переводиться в ноль EBCDIC x'F0'. Если это не было сделано, то (в зависимости от кодовой страницы EBCDIC) x'30'не отображается на действительный символ на большинстве дисплеев EBCDIC.
Однако, даже если они перевели, у вас будут другие проблемы, так как все или некоторые из ваших данных COMP-3 будут повреждены. Простые программы перевода не имеют возможности различить символ и comp-3, поэтому они преобразуют число, такое как x'00303C', в x'00F06E', что заставит любую программу мэйнфрейма разорваться с ужасным "0C7 десятичным арифметическим исключением" (культурно эквивалентно "Stackru").
Таким образом, в основном вы находитесь в ситуации проигрыша / проигрыша. Я бы посоветовал вам отказаться от упакованных десятичных знаков и использовать простые цифры ASCII для своих чисел.
Сжатие не должно вызывать проблем, за исключением того, что утилита передачи файлов, вероятно, выполняла ASCII в EBCDIC для простого текстового файла, но не для заархивированного файла.
Похоже, проблема в преобразовании EBCDIC. Упакованный десятичный знак будет использовать символы в качестве байтовых значений и не подлежит транслитерации EBCDIC <-> ASCII.
Если они видят управляющие символы (или квадратные маркеры в Windows), то они могут просматривать данные ASCII как EBCDIC.
Если вместо "0123456789" они видят "òóôõö øù", то они просматривают символы EBCDIC в средстве просмотра с использованием ANSI или расширенного ASCII.