Nano Hash - криптовалюты, майнинг, программирование

Как проверить число в строке, содержащее дату и экспоненциальные числа, при анализе файла excel с использованием модели событий apache в java

Я разбираю файл excel, который содержит много дат, таких как 13-4-2021, и некоторые числа в этом формате 3,7%, 2,65%. Итак, я разбираю этот файл excel и получаю данные в строке для записи их в текстовом файле. Итак, моя проблема в том, что я получаю дату в виде целого числа, например 44299, хотя на самом деле она находится в формате 13.04.2021 на листе Excel. И еще один случай: у меня есть некоторые числа с процентом, например 3,7%, 2,65%, которые приходят как 3.6999999999999998E-2. Итак, я могу преобразовать число в дату, используя

SimpleDateFormat("MM/dd/yyyy").format(javaDate)

Вот код, который я использую

private static class SheetHandler extends DefaultHandler {
    private SharedStringsTable sst;
    private String lastContents;
    private boolean nextIsString;
    private int rowNumber;

    private SheetHandler(SharedStringsTable sst) {
        this.sst = sst;
    }

    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
        try {

            // row => row
            if(name.equals("row")) {
                   if (attributes.getValue("r") != null) {
                    rowNumber = Integer.valueOf(attributes.getValue("r"));
                   } else {
                    rowNumber++;
                   }
                   //System.out.println("row: " + rowNumber);
                  }

        if (rowNumber > 6) {

        // c => cell
        if(name.equals("c")) {
            // Print the cell reference 

            //System.out.print(attributes.getValue("r") + " - ");
            // Figure out if the value is an index in the SST
            String cellType = attributes.getValue("t");
            if(cellType != null && cellType.equals("s")) {
                nextIsString = true; 
            } else {
                nextIsString = false;
              }

        }
        // Clear contents cache
        lastContents = "";
        }
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    public void endElement(String uri, String localName, String name)
            throws SAXException {
        // Process the last contents as required.
        // Do now, as characters() may be called more than once

        if (rowNumber > 6) {


        if(nextIsString) {
            int idx = Integer.parseInt(lastContents);

            lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
            nextIsString = false;
        }
        // v => contents of a cell
        // Output after we've seen the string contents
        if(name.equals("v")) {
           // System.out.println(lastContents);

            if(!lastContents.isEmpty() ) // Here i am putting the values to a list to process 

                pickUpExcelValues.add(lastContents);
            }
        }
    }

    public void characters(char[] ch, int start, int length)
            throws SAXException {
        lastContents += new String(ch, start, length);
    }
}

Но как я проверю, что строка содержит, скажем, 44299 дату? А также я понятия не имею, как преобразовать это 3,69999999999999998E-2 в 3,7% при записи в текстовый файл. Если у кого-нибудь есть какие-либо идеи, пожалуйста, помогите.


  • Помогите нам помочь вам; опубликовать код Java, обрабатывающий файл Excel. 17.02.2018
  • @Gary'sStudent, пожалуйста, проверьте сообщение, которое я отредактировал 17.02.2018
  • @Gary'sStudent, не могли бы вы проверить сейчас? 18.02.2018
  • Я просмотрел ваш код. Я пытаюсь найти способ получить отформатированную ячейку. Это может занять некоторое время 18.02.2018
  • Нет проблем, я думал, что у тебя нет времени смотреть 18.02.2018
  • @Gary'sStudent, если вы можете управлять полем даты, это тоже нормально, мне пока не нужна экспоненциальная часть 19.02.2018
  • Это только я не понял вашего вопроса? Если ваше число находится в диапазоне от 0 до 1, то это процент (не то же самое, что экспоненциальный). Если это целое число, то это может быть количество дней с 30 декабря 1899 года; но вы должны определить разумный диапазон для ваших дат и отбросить те, которые выходят за его пределы. 19.02.2018
  • Аналогично тому, что @OleV.V. предложил, я бы проверил, содержит ли строка точку (или E) - если да, вам нужно округлить ее до любой интересующей вас десятичной степени. В противном случае это дата в постоянном формате 19.02.2018
  • @ОлеВ.В. я получаю даты в виде числа, например 43256,46586. Поэтому, прежде чем помещать их в список, мне нужно преобразовать их в исходную форму. 19.02.2018
  • @ОлеВ.В. Я не понял вашего решения, можете ли вы написать код для этого, если это возможно? 19.02.2018
  • @GalAbra Могу ли я объяснить свою ситуацию? 19.02.2018
  • Вы не должны использовать SimpleDateFormat. Этот класс не только давно устарел, но и, как известно, доставляет много хлопот. Я рекомендую вместо этого использовать java.time, современный API даты и времени Java. И его DateTimeFormatter класс. 19.02.2018

Ответы:


1

Этот вопрос нуждается в дополнительных пояснениях.

Во-первых, это связано с Как пропустить строки в файле xlsm, используя пользовательскую модель событий apache, на которую был дан ответ.

Но если кто-то хочет использовать примеры из XSSF и SAX (Event API) необходимы базовые знания о XML, используемом в Office Open XML.

А ExampleEventUserModel — это пример очень низкого уровня, демонстрирующий принцип потоковой передачи. Для расширения этого до учета форматов необходимо также проанализировать таблицу стилей, а затем использовать Формат данных.

Ниже приведен полный пример, который делает именно это. Но есть и более полный пример, включая поддержку извлечения информации о форматировании чисел и применения ее к числовым ячейкам (например, для форматирования дат или процентов). См. пример XLSX2CSV в svn.

import java.io.InputStream;
import java.util.Iterator;

import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.BuiltinFormats;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.util.SAXHelper;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;


public class ExampleEventUserModel {
 public void processOneSheet(String filename) throws Exception {
  OPCPackage pkg = OPCPackage.open(filename);
  XSSFReader r = new XSSFReader( pkg );
  SharedStringsTable sst = r.getSharedStringsTable();

  StylesTable st = r.getStylesTable();
  XMLReader parser = fetchSheetParser(sst, st);

  // To look up the Sheet Name / Sheet Order / rID,
  //  you need to process the core Workbook stream.
  // Normally it's of the form rId# or rSheet#
  InputStream sheet2 = r.getSheet("rId2");
  InputSource sheetSource = new InputSource(sheet2);
  parser.parse(sheetSource);
  sheet2.close();
 }

 public void processAllSheets(String filename) throws Exception {
  OPCPackage pkg = OPCPackage.open(filename);
  XSSFReader r = new XSSFReader( pkg );
  SharedStringsTable sst = r.getSharedStringsTable();

  StylesTable st = r.getStylesTable();
  XMLReader parser = fetchSheetParser(sst, st);

  Iterator<InputStream> sheets = r.getSheetsData();
  while(sheets.hasNext()) {
   System.out.println("Processing new sheet:\n");
   InputStream sheet = sheets.next();
   InputSource sheetSource = new InputSource(sheet);
   parser.parse(sheetSource);
   sheet.close();
   System.out.println("");
  }
 }

 public XMLReader fetchSheetParser(SharedStringsTable sst, StylesTable st) throws SAXException, ParserConfigurationException {
/*
  XMLReader parser =
  XMLReaderFactory.createXMLReader(
       "org.apache.xerces.parsers.SAXParser"
  );
*/
  XMLReader parser = SAXHelper.newXMLReader();
  ContentHandler handler = new SheetHandler(sst, st);
  parser.setContentHandler(handler);
  return parser;
 }


 /** 
  * See org.xml.sax.helpers.DefaultHandler javadocs 
  */
 private static class SheetHandler extends DefaultHandler {
  private SharedStringsTable sst;
  private StylesTable st;
  private String lastContents;
  private boolean nextIsString;
  private boolean nextIsStyledNumeric;
  private boolean inlineStr;
  private int styleIndex;
  private DataFormatter formatter;

  private int rowNumber;

  private SheetHandler(SharedStringsTable sst, StylesTable st) {
   this.sst = sst;
   this.st = st;
   this.rowNumber = 0;
   this.formatter = new DataFormatter(java.util.Locale.US, true);
   this.styleIndex = 0;
  }

  public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {

   // row => row
   if(name.equals("row")) {
    if (attributes.getValue("r") != null) {
     rowNumber = Integer.valueOf(attributes.getValue("r"));
    } else {
     rowNumber++;
    }
    System.out.println("row: " + rowNumber);
   }

   if (rowNumber > 6) {

    // c => cell
    if(name.equals("c")) {
     // Print the cell reference
     System.out.print(attributes.getValue("r") + " - ");

     String cellType = attributes.getValue("t");

     // Figure out if the value is an index in the SST
     nextIsString = false;
     if(cellType != null && cellType.equals("s")) {
      nextIsString = true;
     } 

     // Figure out if the value is an inline string     
     inlineStr = false;
     if(cellType != null && cellType.equals("inlineStr")) {
      inlineStr = true;
     } 

     // Figure out if the value is an styled numeric value or date
     nextIsStyledNumeric = false;
     if(cellType != null && cellType.equals("n") || cellType == null) {
      String cellStyle = attributes.getValue("s");
      if (cellStyle != null) {
       styleIndex = Integer.parseInt(cellStyle);
       nextIsStyledNumeric = true;
      }
     } 
    }
   }

   // Clear contents cache
   lastContents = "";
  }

  public void endElement(String uri, String localName, String name)
            throws SAXException {
   if (rowNumber > 6) {

    // Process the last contents as required.
    // Do now, as characters() may be called more than once

    // If the value is in the shared string table, get it
    if(nextIsString) {
     int idx = Integer.parseInt(lastContents);
     lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
     nextIsString = false;
    }

    // v => contents of a cell
    // Output after we've seen the string contents
    if(name.equals("v") || (inlineStr && name.equals("c"))) {
     // If the value is styled numeric, use DataFormatter to formaat it
     if (nextIsStyledNumeric) {
      XSSFCellStyle style = st.getStyleAt(styleIndex);
      int formatIndex = style.getDataFormat();
      String formatString = style.getDataFormatString();
      if (formatString == null) {
       // formatString could not be found, so it must be a builtin format.
       formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
      }
      double value = Double.valueOf(lastContents);
      lastContents = formatter.formatRawCellContents(value, formatIndex, formatString);
      nextIsStyledNumeric = false;
     } 
     // Print out the contents
     System.out.println(lastContents);
    }
   }
  }

  public void characters(char[] ch, int start, int length)
            throws SAXException {
   //collect each character part to the content
   lastContents += new String(ch, start, length);
  }
 }

 public static void main(String[] args) throws Exception {
  ExampleEventUserModel example = new ExampleEventUserModel();
  //example.processOneSheet(args[0]);
  example.processAllSheets(args[0]);
 }
}
20.02.2018
  • Ришар, еще раз спасибо, я недавно использую этот API, поэтому у меня нет особого представления об этом. 21.02.2018

  • 2

    Эта функция оборачивает два случая (процент или дату):

    private static String convert(String s) {
        if(s.indexOf('E') > -1) {   // It's a percentage
            String[] components = s.split("E");
            double num = Double.parseDouble(components[0]) * Math.pow(10, Integer.parseInt(components[1]));
            //return String.valueOf(num);                   // will return i.e. "0.037"
            return Math.round(num * 10000.0) / 100.0 + "%"; // will return i.e. "3.7%"
        }
        else {  // It's a date
            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
            GregorianCalendar gc = new GregorianCalendar(1900, 0, 0);
            gc.add(Calendar.DATE, Integer.parseInt(s) - 1);
            Date date = gc.getTime();
            return sdf.format(date);
        }
    }
    

    Обратите внимание, что даты серийных номеров в Excel представляют собой дни, прошедшие с 1 января 1900 года, поэтому я использовал преобразование.

    Дайте мне знать, как это работает для вас

    19.02.2018
  • Да, я имел в виду что-то подобное. Элегантное округление. Можем ли мы быть уверены, что процент будет содержать E? Может быть 0? Это может быть строчная буква e? И, как я уже сказал, я не одобряю SimpleDateFormat и GregorianCalendar. 19.02.2018
  • @ОлеВ.В. Спасибо за ваш комментарий! Как объяснено здесь заглавная E всегда используется с числами, состоящими более чем из 10 цифр. В общем, я предлагаю вам больше узнать о том, как Excel реализует стандарт IEEE 754. Удачи! 19.02.2018
  • @GalAbra, а как насчет других чисел, которые не являются датой, ваш код будет преобразовывать все в дату 22.02.2018
  • @Mandrek Вы определили входные данные либо как десятичные дроби (представляющие проценты), либо как даты. Если вы хотите разрешить целые числа, вам нужно добавить ограничение для максимального целого числа (выше которого ввод будет обрабатываться как дата) 22.02.2018
  • @GalAbra в другом условии вы предполагаете, что ввод является целым числом, но это не тот случай, когда я получаю число или, скажем, мазню, которая равна 43567 и которая является датой, я должен сначала решить, что это дата, а затем скрывать 22.02.2018
  • @Mandrek Верно, я имел в виду, что вам нужно определить условие, чтобы решить, является ли ваш ввод датой или чем-то еще. 22.02.2018
  • @GalAbra, можете ли вы дать решение этого? 23.02.2018
  • @Mandrek Я уже упоминал, что вы должны указать условие, потому что только вы знаете ожидаемые входные данные 23.02.2018

  • 3

    Мне кажется, что вы можете отличить свои строки от Excel примерно так:

    private static void checkNumber(String fromExcel) {
        try {
            double asNumber = Double.parseDouble(fromExcel);
            if (asNumber >= 0 && asNumber <= 1) {
                System.out.println("Percentage: " + asNumber * 100 + " %");
            }
            long asWholeNumber = Math.round(asNumber);
            try {
                LocalDate asDate = LocalDate.of(1899, Month.DECEMBER, 30)
                        .plusDays(asWholeNumber);
                if (asDate.isAfter(LocalDate.of(2000, Month.DECEMBER, 31)) 
                        && asDate.isBefore(LocalDate.of(2035, Month.JANUARY, 1))) {
                    System.out.println("Date: " + asDate);
                }
            } catch (DateTimeException dte) {
                System.out.println("Unidentified: " + fromExcel);
            }
        } catch (NumberFormatException nfe) {
            System.out.println("Unidentified: " + fromExcel);
        }
    }
    

    Попробуйте этот метод:

        checkNumber("44299");
        checkNumber("3.6999999999999998E-2");
    

    Это печатает:

    Date: 2021-04-13
    Percentage: 3.6999999999999997 %
    

    Наличие двух возможных интерпретаций строк не должно мешать вам выполнять проверку и отлавливать строки, которые не соответствуют ни одной из интерпретаций, поэтому я пытаюсь фильтровать каждый случай. Обратите внимание, что если вы включите 1899 год в число принятых дат, «0» и «1» будут приняты как даты, так и проценты.

    Я использую и рекомендую LocalDate из java.time, современный API даты и времени Java, для обработки дат. С современным API гораздо приятнее работать, чем с устаревшими Date и GregorianCalendar.

    Идея Гэри Студента получить отформатированную ячейку может быть более правильным путем.

    Вопрос: Мой код должен быть совместим с Java 6; я могу использовать java.time?

    EDIT: Да, java.time может хорошо работать в Java 6.

    • В Java 8 и более поздних версиях, а также на новых устройствах Android (как мне сказали, начиная с уровня API 26) новый API уже встроен.
    • В Java 6 и 7 добавлена ​​версия ThreeTen Backport, версия новых классов (ThreeTen для JSR 310, где впервые был описан современный API). EDIT 2: убедитесь, что вы импортируете классы даты и времени и исключения из пакета org.threeten.bp и подпакетов.
    • На (старом) Android используйте версию ThreeTen Backport для Android. Он называется ThreeTenABP. Также здесь убедитесь, что вы импортируете классы даты и времени из пакета org.threeten.bp и подпакетов.

    Ссылки

    19.02.2018
  • В.В. это для java 8? Мне нужен код, совместимый с Java 6 22.02.2018
  • В.В. Вы редактировали код? потому что я думаю, что LocalDate - это проблема, которая не работает в Java 6 22.02.2018
  • В.В. Я не вижу никаких изменений в коде, который вы редактировали? 22.02.2018
  • Для Java 6 никаких изменений в коде не требуется (поэтому я ничего не делал). Я добавил пару разделов внизу с объяснением. 22.02.2018
  • я получил это исключение Действие номер 3 готово к обработке. Ниже приведены входные параметры: 1. inputFile= E:\Oracle\FDMEEDATA\MANREP/inbox/MREP_File_Dummy/NNLife-January2018MarketValueReport.xlsx 2. outputFile= E:\Oracle\FDMEEDATA\ MANREP/inbox/MREP_File_Dummy/out.txt 3. monthName = Dec'17 Exception in thread main java.lang.NoClassDefFoundError: java/time/DateTimeException at com.excelmanager.ActivityLauncher.main(ActivityLauncher.java:68) 22.02.2018
  • В.В., тогда почему я получаю DateTimeException? 22.02.2018
  • Ах, при использовании бэкпорта нужно импортировать из org.threeten.bp, например import org.threeten.bp.DateTimeException;. Я снова отредактировал (вы не получали DateTimeException, вы получали NoClassDefFoundError, но это точно не лучше). 22.02.2018
  • получение этого java.lang.NoClassDefFoundError: java/time/chrono/ChronoLocalDate 22.02.2018
  • Давайте продолжим обсуждение в чате. 22.02.2018
  • В. В. Я компилирую с использованием Java 6, поэтому я видел, что это происходит только тогда, когда у вас нет Java 8. 23.02.2018
  • Я все еще в чате (проверяю пару раз в день еще день-два). 23.02.2018
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..