【问题标题】:Validate decimal number验证十进制数
【发布时间】:2015-06-16 12:48:54
【问题描述】:

我正在阅读一些 .csv 文件,其中包含表示十进制数的字符串。我的麻烦是很多时候我正在使用不同的语言环境接收文件写入。例如:

  1. file1.csv 列 price 的值为 129,13(, 为小数分隔符)
  2. file1.csv 列 price 的值为 129.13(. 为小数分隔符)

现在我正在尝试以这种方式读取值:

 DecimalFormatSymbols dfs = new DecimalFormatSymbols(new Locale(en,US));
 DecimalFormat df= new DecimalFormat();
 df.setDecimalFormatSymbols(dfs);
 df.setParseBigDecimal(true);
 bigDecimal = (BigDecimal) df.parse(value);

使用这个 sn-p 代码,第一个值变成 12913(不正确),而第二个变成 129.13(正确)。现在我希望如果我使用 en_US local 并且文件包含使用的值,例如小数点分隔符,我必须抛出异常。

我该怎么做?

【问题讨论】:

  • 您可以尝试使用dfs.getDecimalSeparator() 使用动态构建的正则表达式首先检查值,对于英语,这可能会导致类似\d+(\.\d{1,2})? 的表达式,这将允许使用点作为整数和十进制值小数点分隔符,允许 1 到 2 个小数位。
  • 这有点奇怪,您没有统一的方式来格式化这些数字。 CSV 文件的分隔符是什么?
  • @Tom 我不知道分隔符分隔符,因为我的系统与可以以两种方式生成文件的外部系统交互。
  • 那你怎么知道,是列之间的分隔符还是数字的符号?

标签: java csv decimalformat


【解决方案1】:

虽然在使用DecimalFormatSymbols 时不能为组分隔符设置任何内容(null)(因为它是char),但您可以将其设置为在有效数字中非常不寻常的值,例如'@ '。

 DecimalFormatSymbols dfs = new DecimalFormatSymbols(new Locale(en,US));
 dfs.setGroupingSeparator('@');
 DecimalFormat df= new DecimalFormat();
 df.setDecimalFormatSymbols(dfs);
 df.setParseBigDecimal(true);
 bigDecimal = (BigDecimal) df.parse(value);

【讨论】:

  • 如果您不将 Locale 提供给 DecimalFormatSymbols 构造函数会发生什么情况,例如DecimalFormatSymbols dfs = new DecimalFormatSymbols();
  • 默认本地,但结果不变
【解决方案2】:

来自Java Tutorials

DecimalFormatSymbols unusualSymbols = new DecimalFormatSymbols(currentLocale);
unusualSymbols.setDecimalSeparator('|');
unusualSymbols.setGroupingSeparator('^');

String strange = "#,##0.###";
DecimalFormat weirdFormatter = new DecimalFormat(strange, unusualSymbols);
weirdFormatter.setGroupingSize(4);

您需要设置 DeciamlFormat 模式才能将它们组合在一起。

DecimalFormatSymbols dfs = new DecimalFormatSymbols(new Locale(en,US));
dfs.setGroupingSeparator('@');

DecimalFormat df= new DecimalFormat(#,###.#", dfs);
df.setParseBigDecimal(true);

bigDecimal = (BigDecimal) df.parse(value);

【讨论】:

  • 129,13 变成 129
  • 我想这个问题没有“漂亮”的解决方案。 OP 获取一个带有未知分隔符的 CSV 文件,并以 ",""." 作为小数分隔符。他可以做类似string.replace(",", "."); 的事情,但这远非“最佳”。最好的解决方案是,如果 OP 在当前文件中具有哪个符号具有哪个含义的信息,或者他可以以统一的结构获取这些文件。
【解决方案3】:

您可以通过univocity-parsers 阅读您的 CSV。

我们仍在开发 2.0 版,它引入了格式自动检测,但您已经可以获取快照版本并使用它来处理此问题。

简单示例:

public static void main(String... args) {

    CsvParserSettings parserSettings = new CsvParserSettings();
    parserSettings.detectFormatAutomatically();

    List<String[]> rows = new CsvParser(parserSettings).parseAll(new StringReader("Amount,Tax,Total\n1.99,10.0,2.189\n5,20.0,6"));
    for (Object[] row : rows) {
        System.out.println(Arrays.toString(row));
    }

    System.out.println("####");

    rows = new CsvParser(parserSettings).parseAll(new StringReader("Amount;Tax;Total\n1,99;10,0;2,189\n5;20,0;6"));
    for (Object[] row : rows) {
        System.out.println(Arrays.toString(row));
    }
}

输出:

[Amount, Tax, Total]
[1.99, 10.0, 2.189]
[5, 20.0, 6]
####
[Amount, Tax, Total]
[1,99, 10,0, 2,189]
[5, 20,0, 6]

您可以从here获取最新的快照版本。

或者,如果您使用 maven,请将其添加到您的 pom.xml

<repositories>
    <repository>
        <id>ossrh</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </repository>
</repositories>

并将版本设置为 2.0.0-SNAPSHOT:

<dependency>
        <groupId>com.univocity</groupId>
        <artifactId>univocity-parsers</artifactId>
        <version>2.0.0-SNAPSHOT</version>
</dependency>

如果您发现任何问题,只需打开一个新问题in the project's github page

编辑:另一个示例演示如何使用多个格式化程序将输入行转换为 BigDecimal:

public static void main(String... args) {
    // ObjectRowListProcessor converts the parsed values and stores the result in a list.
    ObjectRowListProcessor rowProcessor = new ObjectRowListProcessor();

    FormattedBigDecimalConversion conversion = new FormattedBigDecimalConversion();
    conversion.addFormat("0.00", "decimalSeparator=.");
    conversion.addFormat("0,00", "decimalSeparator=,");

    // Here we convert fields at columns 0, 1 and 2 to BigDecimal, using two possible input formats 
    rowProcessor.convertIndexes(conversion).set(0, 1, 2);

    // Create a settings object to configure the CSV parser
    CsvParserSettings parserSettings = new CsvParserSettings();

    //I'll separate the values using | to make it easier for you to identify the values in the input
    parserSettings.getFormat().setDelimiter('|');

    // We want to use the RowProcessor configured above to parse our data 
    parserSettings.setRowProcessor(rowProcessor);


    // Create the parser
    CsvParser parser = new CsvParser(parserSettings);

    // Parse everything. All rows are sent to the rowProcessor configured above
    parser.parse(new StringReader("1.99|10.0|2.189\n1,99|10,0|2,189"));

    // Let's get the parsed rows
    List<Object[]> rows = rowProcessor.getRows();
    for (Object[] row : rows) {
        System.out.println(Arrays.toString(row));
    }
}

这是输出:2 个包含 BigDecimal 对象的数组,以及正确的值:

[1.99, 10.0, 2.189]
[1.99, 10.0, 2.189]

【讨论】:

  • 该解析器能否处理这种情况,即 CSV 中没有“标题”,因此数字的 ,. 是它可以“到达”的第一个可能的分隔符?
  • 是的,没问题。默认情况下,它无论如何都不会读取标题。我将它们添加到我的示例中以使其更易于阅读。
  • 那它是如何决定哪个字符是分隔符的呢?
  • 它在开始解析过程之前分析输入的一部分。基本上,它会收集一些输入行中出现候选字符的统计信息。如果相同的字符出现在所有(有效)行中,且差异很小(例如,每行中总是有 10-11 个逗号),则选择它作为分隔符。它还检测行分隔符、引号和引号转义。试试看!
猜你喜欢
  • 2011-01-30
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 1970-01-01
  • 2016-11-15
相关资源
最近更新 更多