获取单元格值,以及它在excel中的显示方式

时间:2013-10-16 10:45:23

标签: java apache-poi

我目前正在开发一个使用Apache POI读取excel文件的项目。

我的任务似乎很简单,我只需要获取excel文件中显示的单元格值。我知道根据单元格的单元格类型执行switch语句。但如果数据类似于

9,000.00

当我9000.0时,POI会给我getNumericCellValue()。当我强制单元格为字符串类型并执行getStringCellValue()时,它会给我9000。我需要的是数据如何在excel中呈现。

我发现一些帖子告诉我使用DataFormat课程,但根据我的理解,它要求您的代码知道该单元格的格式。在我的情况下,我不知道单元格可能具有的格式。

那么,我如何检索单元格值在excel中的显示方式?

3 个答案:

答案 0 :(得分:19)

Excel将一些单元格存储为字符串,但大多数都是应用了特殊格式规则的数字。您需要做的是对数字单元格运行这些格式规则,以生成与Excel中的字符串类似的字符串。

幸运的是,Apache POI有一个专门的课程 - DataFormatter

您需要做的就是:

 Workbook wb = WorkbookFactory.create(new File("myfile.xls"));
 DataFormatter df = new DataFormatter();

 Sheet s = wb.getSheetAt(0);
 Row r1 = s.getRow(0);
 Cell cA1 = r1.getCell(0);

 String asItLooksInExcel = df.formatCellValue(cA1);

无论细胞类型是什么,DataFormatter都会使用Excel中应用的规则为您尽可能地格式化它

答案 1 :(得分:1)

实际上,用单元格在写入时定义的语言环境永远不可能获得准确的格式化单元格值。这是由于存在区域设置观察者,而且以后格式化时,内部区域设置excel前缀从未重用;

对POI 3.17的分析(可能会有所变化,因为要看内部是如何完成组件的)

例如:Locale.US的单元格样式(来自CellStyle.getDataFormatString())的dateConverted格式为dd MMM yyyy hh:mm:ss的格式是:
“ [$ -0409] dd MMM yyyy hh:mm:ss; @“    其中本地excel内部前缀= [$ -0409]

它是从DateFormatConverter.localPrefixes私有静态映射获得的。

以下是一些可解决此问题的代码:

/**
 * Missing method in POI to enable the visualisation asIs of an cell with a
 * different locale in a xls document.
 *
 * @param style
 *            the cell style localized.
 * @return the Locale found using internal locationPrefixes.
 */
private final Locale extractLocaleFromDateCellStyle(final CellStyle style) {

    final String reOpenedFormat = style.getDataFormatString();

    LOGGER.info("Data Format of CellStyle : " + reOpenedFormat);
    Locale locale = getLocaleFromPrefixes(extractPrefixeFromPattern(reOpenedFormat));
    LOGGER.info("Found locale : " + locale);
    return locale;
}

/**
 * Extracts the internal prefix that represent the local of the style.
 *
 * @param pattern
 *            the DataFormatString of the cell style.
 * @return the prefix found.
 */
private final String extractPrefixeFromPattern(final String pattern) {

    Pattern regex = Pattern.compile(REGEX_PREFIX_PATTERN);
    Matcher match = regex.matcher(pattern);

    if (match.find()) {
        LOGGER.info("Found prefix: " + match.group(1));
        // return only the prefix
        return match.group(1);
    }
    return null;
}

/**
 * Reverse obtain the locale from the internal prefix from
 * <code>DateFormatConverter.localePrefixes</code> private static field.
 * <p>
 * Uses reflection API.
 *
 * @param prefixes
 *            the prefixes
 * @return the local corresponding tho the prefixes.
 */
public static Locale getLocaleFromPrefixes(final String prefixes) {

    try {

        @SuppressWarnings("unchecked")
        Map<String, String> map = getStaticPrivateInternalMapLocalePrefix();

        String localPrefix = null;

        // find the language_Country value matching the internal excel
        // prefix.
        for (Map.Entry<String, String> entry : map.entrySet()) {

            LOGGER.info("value : " + entry.getValue() + ", key :"
                    + entry.getKey());

            if (entry.getValue().equals(prefixes)
                    && !StringUtils.isBlank(entry.getKey())) {
                localPrefix = entry.getKey();
                break;
            }
        }

        // Generate a Locale with language, uppercase(country) info.
        LOGGER.info(localPrefix);
        if (localPrefix.indexOf('_') > 0) {
            String[] languageCountry = localPrefix.split("_");
            return new Locale(languageCountry[0],
                    StringUtils.defaultString(languageCountry[1]
                            .toUpperCase()));
        }

        // nothing found.
        return null;

        // factorized the multiples exceptions.
    } catch (Exception e) {
        throw new UnsupportedOperationException(e);
    }

}

/**
 * gets the internal code map for locale used by Excel.
 * 
 * @return the internal map.
 * @throws NoSuchFieldException
 *             if the private field name changes.
 * @throws IllegalAccessException
 *             if the accessible is restricted.
 */
private static Map<String, String> getStaticPrivateInternalMapLocalePrefix()
        throws NoSuchFieldException, IllegalAccessException {
    // REFLECTION
    Class<?> clazz = DateFormatConverter.class;
    Field fieldlocalPrefixes = (Field) clazz
            .getDeclaredField(DATE_CONVERTER_PRIVATE_PREFIXES_MAP);

    // change from private to public.
    fieldlocalPrefixes.setAccessible(true);

    @SuppressWarnings("unchecked")
    Map<String, String> map = (Map<String, String>) fieldlocalPrefixes
            .get(clazz);

    LOGGER.info("MAP localPrefixes : " + map);
    return map;
}

因此,下面的简单代码应该可以解决问题。 请注意,该代码尚未针对空值进行全面测试,并且取决于您使用的POI版本,直到它们在此处更改 LOCALE OBSERVER MADNESS :)

    ....
    final CellStyle cellStyle = reopenedCell.getCellStyle();

    Locale locale = extractLocaleFromDateCellStyle(cellStyle);
    LOGGER.info("FOUND LOCAL : " + locale);

    // use the same local from the cell style during writing.
    DataFormatter df = new DataFormatter(locale);

    String reOpenValue = df.formatCellValue(reopenedCell);

致谢。

答案 2 :(得分:0)

使用CellType可以查看所有内容

if (cellValue.getCellType() == Cell.CELL_TYPE_NUMERIC)

// cellValue是数字

if (cellValue.getCellType() == Cell.CELL_TYPE_STRING)

// cellValue是一个字符串

日期也以数字形式给出,那时使用dateUtil

检查给定单元格是否为日期
if (DateUtil.isCellDateFormatted(cellData))

之后可以将cellvalue转换为日期