2015-03-06 1 views
0

У меня есть файл, и вот часть файла. Общее слово во всех строках - это PIC, и я могу узнать индекс PIC. Я пытаюсь извлечь описание для каждой строки. Здесь, как я могу извлечь слово перед словом PIC?Найти слово в строке, основанной на следующем слове

  15 EXTR-SITE         PIC X. 
     05 EXTR-DBA         PIC X. 
TE0305*  05 EXTR-BRANCH       PIC X(05). 
TE0305*  05 EXTR-NUMBER       PIC X(06). 
TE0305  05 FILLER         PIC X(11). 
CW0104  10 EXTR-TEXT6      PIC X(67). 
CW0104  10 EXTR-TEXT7      PIC X(67). 
CW0104*  05 FILLER         PIC X(567). 

я должен получить результат, как показано ниже

EXTR-SITE 
EXTR-DBA 
EXTR-NUMBER 
------- 
FILLER 

Есть ли выражение, которое я могу использовать, чтобы найти слово, прежде чем «ПИК»?

Вот мой код, чтобы получить строки, содержащие 'ПИК':

int wordStartIndex = line.indexOf("PIC"); 
int wordEndIndex = line.indexOf("."); 
if ((wordStartIndex > -1) && (wordEndIndex >= wordStartIndex)) { 
System.out.println(line); } 
+0

попробуйте использовать функцию String.split –

+1

Просто добавьте предупреждения. Если вы пытаетесь разобрать COBOL, будьте осторожны с 'PIC' как псевдоним' PICTURE' и смотрите имена полей, которые могут содержать PIC в любом месте имени. Может быть, беспокоиться также о том, чтобы вырезать строки комментариев, '*' в столбце 7 (или 1 для некоторых компиляторов произвольной формы, но поддержка этого расширения может привести к неоднозначному разбору кода, поэтому boo hiss при его использовании) и очень рекомендуется '* > ', пропустите до конца маркеров комментария строки. –

+2

Чтобы добавить к предупреждениям Брайана: нет требования, чтобы имя элемента находилось на той же строке, что и PIC/PICTURE; нет требования, чтобы элемент имел PIC/PICTURE; нет требования, чтобы элемент имел имя; нет требования, чтобы имя было данными непосредственно перед PIC/PICTURE. Образец, который вы показываете, даже не компилируется, 'FILLER' после EXTR-NUMBER не должен иметь PIC/PICTURE. –

ответ

1

Вы можете попробовать регулярное выражение, как это:

public static void main(String[] args) { 
    String s = "15 EXTR-SITE         PIC X."; 
    System.out.println(s.replaceAll("(.*?\\s+)+(.*?)(?=\\s+PIC).*", "$1")); 
} 

O/P:

EXTR-SITE 

Объяснение :

(.*?\\s+)+(.*?)(?=\\s+PIC).*", "$1") : 

(.*?\\s+)+ --> Find one or more groups of "anything" which is followed by a space. 

(.*?)(?=\\s+PIC) -->find a group of "any set of characters" which are followed by a space and the word "PIC". 
.* --> Select everything after PIC. 

$1 --> the contents of the actual String with the first captured group i.e, data between `()`. 

PS: Это работает со всеми текущими входами: P

+0

@ TheLostMind-я действительно ценю все ответы здесь, но я вижу, что этот ответ прост и работает идеально в моем случае. Если вы можете дать краткое объяснение выражения, это будет здорово. – SamK

+0

@KodS - отредактировал ответ, чтобы дать объяснение :) – TheLostMind

0
//let 'lines' be an array of all your lines 
//with one complete line as string per element 

for(String line : lines){ 
    String[] splitted = line.split(" "); 
    for(int i = 0; i < splitted.length; i++){ 
     if(splitted[i].equals("PIC") && i > 0) System.out.println(splitted[i-1]); 
    } 
} 

Пожалуйста, обратите внимание, что я не проверял этот код еще (но в течение нескольких минут). Однако общий подход теперь будет ясен.

+1

добавьте это в свой оператор 'if'' && i> 0) ' –

+0

Почему вы не используете' Arrays.asList (splitted) ', а затем метод' indexOf' ? –

+0

@ riccardo.cardin Что вы получаете? –

0

Попробуйте использовать String.split("\\s+"). Этот метод разбивает исходную строку на массив строк (String[]). Затем, используя Arrays.asList(...), вы можете преобразовать свой массив в List, чтобы вы могли искать определенный объект, используя indexOf.

Вот выдержка из раствора possibile:

String words = "TE0305*  05 EXTR-BRANCH       PIC X(05)."; 
List<String> list = Arrays.asList(words.split("\\s+")); 
int index = list.indexOf("PIC"); 
// Prints EXTR-BRANCH 
System.out.println(index > 0 ? list.get(index - 1) : ""); // Added a guard 

По моему честному мнению, этот код позволяет Java работать для вас, а не наоборот. Это кратким, читаемым, а затем более ремонтопригодным.

3
  15 EXTR-SITE         PIC X. 
     05 EXTR-DBA         PIC X. 
TE0305*  05 EXTR-BRANCH       PIC X(05). 
TE0305*  05 EXTR-NUMBER       PIC X(06). 
TE0305  05 FILLER         PIC X(11). 
CW0104  10 EXTR-TEXT6      PIC X(67). 
CW0104  10 EXTR-TEXT7      PIC X(67). 
CW0104*  05 FILLER         PIC X(567). 

Я думаю, вам нужно больше узнать о COBOL, прежде чем подходить к этой задаче.

Столбцы 1-6 могут содержать порядковый номер, могут быть пустыми или содержать что угодно. Если вы пытаетесь разобрать код COBOL, вам нужно игнорировать столбцы 1-6.

Столбец 7 называется областью индикатора. Он может быть пустым или содержать *, который указывает комментарий, или -, который указывает, что строка является продолжением предыдущей строки без пробелов/без комментариев или содержит D, которая указывает, что это строка отладки.

Столбцы 73-80 могут содержать другой порядковый номер или пробел или что-либо еще и должны быть проигнорированы.

Если ваш источник COBOL был «свободным форматом», все будет немного по-другому, но это не так.

Нет смысла извлекать данные из строк комментариев, поэтому ожидаемый результат недействителен. Также неясно, где вы получаете линию тире в ожидаемом выходе.

Если вы пытаетесь разобрать источник COBOL, у вас должен быть действительный источник COBOL. Это не является действительным:

TE0305  05 FILLER         PIC X(11). 
CW0104  10 EXTR-TEXT6      PIC X(67). 
CW0104  10 EXTR-TEXT7      PIC X(67). 

Уровень-число (05) представляет собой группу элемент, если за ней следует более высокого уровня числами (два 10S). Группа не может иметь PICture.

PIC сам также может быть написан полностью, как ИЗОБРАЖЕНИЕ.

PIC может легко отображаться в идентификаторе/имени-данных (EPIC-CODE). Как и ИЗОБРАЖЕНИЕ, теоретически.

PIC и PICTURE могут отображаться в строке комментариев, даже если не прокомментированная строка кода.

Недостаток метода, который вы хотите использовать для поиска «описания» (который является идентификатором или именем данных).

01 the-record. 
     05 fixed-part-of-record. 
      10 an-individual-item PIC X. 
      10 another-item COMP-1. 
      10 and-another COMP-3 PIC 9(3). 
      10 PIC X. 
     05 variable-part-of-record. 
      10 entry-name OCCURS 10 TIMES. 
       15 entry-name-client-first-name 
            PIC X(30). 
       15 entry-name-client-surname 
            PIC X(30). 

Это всего лишь короткий пример, чтобы не считаться всеобъемлющим.

От того, что ваш метод будет получить

an-individual-item 
COMP-3 
and two lines of "whatever happens when PIC is the first thing on line" 

Чтобы сохранить это становится хамелеона вопрос, вам нужно задать новый вопрос (или разбирайтесь сами) с другим методом.

В зависимости от источника источника COBOL существуют лучшие способы борьбы с этим. Если источником является COBOL для IBM Mainframe, источником для вашего источника должен быть либо компиляционный список, либо SYSADATA из компиляции.

Из любого из них вы получите идентификатор/имя-имя в определенном месте в определенном состоянии. Никакой разборки вообще не нужно.

Если вы не можете это получить, я предлагаю вам найти номер уровня и найти первое, что после этого. У вас все еще будет некоторая работа.

Номера уровней могут быть одной или двумя цифрами в диапазоне 1-49, плюс 66, 77, 88. Некоторые компиляторы также имеют 78. Если ваш экстракт - это только «записи» (скорее всего), вы не увидите 77 или 78. Вероятнее всего, вы не увидите 66 (только видели, как он использовался один раз), и, вероятно, он увидит 88s, которые вы можете или не хотите включать в свой вывод (в зависимости от того, для чего вам это нужно).

1. 
01. 
01 FILLER. 
01 data-name-name-1. 
01 data-name-name-2 PIC X(80). 
    5. 
    05. 
    05 FILLER. 
    05 FILLER PIC X. 
    05 data-name-name-3. 
    05 data-name-name-4 PIC X. 

Использование одной цифры для уровня-числа, а не написание FILLER явно довольно «новый» (от 1985 Standard), и это вполне возможно, у вас нет какой-либо из них. Но ты можешь.

Выход из вышеизложенного следует:

FILLER 
FILLER 
FILLER 
data-name-name-1 
data-name-name-2 
FILLER 
FILLER 
FILLER 
FILLER 
data-name-name-3 
data-name-name-4 

Я понятия не имею, что вы хотите сделать с этим выходом. Без контекста это не имеет большого значения.

Возможно, ваш выбранный метод будет работать с вашими фактическими данными (при условии, что вы выбрали образец, и то, что вы получаете, является действительным кодом).

Однако было бы проще сказать «если первое слово в строке одно или двухзначное числовое, если есть второе слово, это то, что мы хотим, иначе используйте FILLER». Отмечая, конечно, предыдущие комментарии о том, что вы должны игнорировать.

Если ваш источник не содержит 88 уровней. Поскольку для целого ряда значений потребуется вторая строка, и если значения будут числовыми и одной или двумя цифрами, то это тоже не сработает.

Итак, укажите источник вашего источника. Если это мэйнфрейм IBM, попытайтесь получить результат из компиляции. Тогда ваша задача очень проста и на 100% точна.

Если вы не можете это получить, ознакомьтесь с этими данными. Если у вас действительно простые структуры, чтобы ваш метод работал, выполнение его с номера уровня все равно будет проще.

Если вам нужно вернуться к этому вопросу, задайте новый вопрос. В противном случае вы болтаете, чтобы высушить людей, которые уже провели свое время добровольно, отвечая на ваш существующий вопрос.

+0

@ Bill-Спасибо за подробное объяснение формата cobol. Для этого конкретного проекта ошибка с комментарием PIC или PICTURE не входит в сферу действия, а также максимальное количество копировальных файлов ограничено 20. Я пытаюсь прояснить один из вашего ответа. «попытаться получить результат из компиляции», что вы имеете в виду из самого компилятора IBM? неясно, какая часть. – SamK

+2

Если источником является мэйнфрейм, в выходном листе компилятора есть форматированный вывод, который вы можете использовать, когда компилятор уже выполнил сложный синтаксический анализ. Также возможно, чтобы компилятор подготовил выходной файл SYSADATA, который снова предоставит вам легкий доступ к необходимой вам информации. Если списки компиляции хранятся для программ, содержащих ваши исходные данные, ваша задача становится тривиальной. Если не хранить, требуют некоторых повторных компиляций, а затем кому-то нужно это сделать за небольшую плату. Гораздо лучший путь, чем вы пытаетесь написать часть компилятора COBOL. –

+1

Просто еще одно примечание. Для GnuCOBOL прохождение протокола препроцессора проще анализировать. Как отметил Билл, гибкие и сложные правила синтаксиса COBOL, в контексте, рассматриваются компилятором, а не контекстными регулярными выражениями. 'cobc -E' создает более удобный для анализа файл листинга при использовании GnuCOBOL. И хотя совпадения регулярных выражений никогда не могут быть на 100%, пост-препроцессорные файлы значительно увеличивают вероятность * менее ошибочного *. –

3

Если вы не совершали к написанию Cobol анализатору себя, несколько вариантов включают в себя:

  • Используйте Cobol Compiler обрабатывать тетрадку Cobol. Это создаст список Cobol-Copybook в формате, который будет проще анализировать. Я работал в компаниях, которые полностью конвертировали Cobol -Copybooks в эквивалентные учебники для простого чтения, компилируя Cobol-Copybook в программе типа Hello-World и обрабатывая результат.

  • Продукты, такие как Файл-Aid имеют анализаторы Cobol, которые производят легко усваиваемую версию Cobol Copybook.

  • Проект java cb2xml преобразует Cobol-Copybook в Xml. В проекте представлены некоторые примеры обработки Xml с помощью Jaxb.


Чтобы разобрать Cobol-прописи в список Java элементов с помощью cb2xml (взятый из Demo2.java):

JAXBContext jc = JAXBContext.newInstance(Condition.class, Copybook.class, Item.class); 
Unmarshaller unmarshaller = jc.createUnmarshaller(); 
Document doc = Cb2Xml2.convertToXMLDOM(
         new File(Code.getFullName("BitOfEverything.cbl").getFile()), 
         false, 
         Cb2xmlConstants.USE_STANDARD_COLUMNS); 
JAXBElement<Copybook> copybook = unmarshaller.unmarshal(doc, Copybook.class); 

Программа Demo2.java затем распечатать содержимое COBOL выписка из книги:

List<Item> items = copybook.getValue().getItem(); 
for (Item item : items) { 
    Code.printItem(" ", item); 
} 

A й для печати Cobol-Item Code.java:

public static void printItem(String indent, Item item) { 
    char[] nc = new char[Math.max(1, 50 - indent.length() 
             - item.getName().length())]; 
    String picture = item.getPicture(); 
    Arrays.fill(nc, ' '); 
    if (picture == null) { 
     picture = ""; 
    } 
    System.out.println(indent + item.getLevel() + " " + item.getName() 
       + new String(nc) + item.getPosition() 
       + " " + item.getStorageLength() + "\t" + picture); 
    List<Item> childItems = item.getItem(); 
    for (Item child : childItems) { 
     printItem(indent + " ", child); 
    } 
} 

Выход из demo2, как (дает имя уровня, поля, начало, длина и рисунок):

 
    01 CompFields          1 5099 
     03 NumA          1 25 --,---,---,---,---,--9.99 
     03 NumB          26 3 9V99 
     03 NumC          29 3 999 
     03 text          32 20 x(20) 
     03 NumD          52 3 VPPP999 
     03 NumE          55 3 999PPP 
     03 float          58 4 
     03 double          62 8 
     03 filler          70 23 
     05 RBI-REPETITIVE-AREA      70 13 
      10 RBI-REPEAT       70 13 
       15 RBI-NUMBER-S96SLS     70 7 S9(06) 
       15 RBI-NUMBER-S96DISP     77 6 S9(06) 
     05 SFIELD-SEP        83 10 S9(7)V99 

Другой cb2xml пример DemoCobolJTreeTable.java, который отображает COBOL тетрадь в таблице деревьев:

Cobol-Copybook displayed in a Tree Table

+0

Я очень ценю ваш ответ. Я искал какое-то время, чтобы получить хороший парсер cobol. Я попробую вашу рекомендацию. Поскольку я уже выбрал лучший ответ раньше, я вырвал ваш ответ. – SamK

+0

Вы работаете на хостеле? У вашего магазина есть File-Aid или аналогичный? – mckenzm