【问题标题】:Determine user input data-type dynamically in JAVA在 JAVA 中动态确定用户输入数据类型
【发布时间】:2018-02-08 10:23:55
【问题描述】:

我编写了以下代码来确定用户输入的数据类型。

更新:删除了对Float 的解析,因为Double 值也可以解析为Float,代价是@DodgyCodeException 提到的一些精度

import java.util.Scanner;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class Main {

    public static void main(String[] args) {
        Scanner src = new Scanner(System.in);
        String input;
        System.out.println("Enter input:");
        input = src.nextLine();
        System.out.println("You entered:" + getDataType(input));
    }

    private static String getDataType(Object input) {
        try {
            JSONObject obj = new JSONObject((String) input);
            return "JSONObject";
        } catch (JSONException ex) {
            try {
                JSONArray array = new JSONArray(input);
                return "JSONArray";
            } catch (JSONException ex0) {
                try {    
                    Integer inti = Integer.parseInt((String) input);
                    return "Integer";
                } catch (NumberFormatException ex1) {
                    try {                          
                            Double dub = Double.parseDouble((String) input);
                            return "Double";
                        } catch (NumberFormatException ex3) {
                            return "String";
                        }
                    }
                }
            }
        }
    }
}

我必须重复运行数百次,并且我读过捕获 Exception 是一项昂贵的操作。
有没有更好的方法来实现这一点?

【问题讨论】:

  • 您是否尝试过,在哪里可以使用 instanceof?
  • 这是错误的做法。您不必优化您编写的所有内容。尤其是不要过早
  • 您似乎正在投射到String willy-nilly 没有赶上ClassCastException。如果你知道它肯定是一个字符串,为什么不把参数变成一个字符串呢?
  • 成功解析为float 并不意味着该数字不能是精度更高的double。解析为float 只会丢弃字符串中存在的额外无法表示的精度。你最好总是解析为double
  • @Jayanth 您应该先阅读其他答案,抱歉,instanceOf 无济于事。

标签: java json


【解决方案1】:

我的方法是让框架做它的事情,并使用它以一种通用的方式解析输入:

Object obj = new org.json.JSONTokener(input).nextValue();
if (obj instanceof JSONArray)
    return "JSONArray";

if (obj instanceof JSONObject)
    return "JSONObject";

if (obj instanceof Integer)
    return "Integer"
...

也许使用switch 语句或Map 而不是一长串if 构造。并捕获 JSONException 的无效 JSON 输入。

也许可以简化为返回 obj.getClass()obj.getClass().getSimpleName() 以获得与您现在拥有的几乎相同的功能。

【讨论】:

  • 我想你的答案是迄今为止最好的,我在this 看到的答案与你上面所说的略有不同。我还检查了visitor pattern,但对于这个相当简单的任务来说,这似乎太辛苦了。
【解决方案2】:

老实说,我不喜欢通过解析器的异常来确定类型的想法。我会为每种格式构建一个模式并遍历所有格式以检查给定的字符串是否与其中一个匹配。

这与Scanner 类的工作方式类似,但相当复杂。很难写出涵盖所有可能情况的正则表达式。

但如果准备好正则表达式,算法就很简单了:

public class Main {

    private final Map<String, String> patterns = Map.of(
            "Integer", integerPattern()
    );

    public String getDataType(String input) {
        for (Map.Entry<String, String> entry : patterns.entrySet()) {
            if (Pattern.matches(entry.getValue(), input)) {
                return entry.getKey();
            }
        }
        return "Unknown";
    }

}

这是一个有趣的部分——integerPattern() 的一个例子。我取自Scanner 并稍微简化了一下:

private String integerPattern() {
    String radixDigits = "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, 10);
    String digit = "((?i)[" + radixDigits + "]|\\p{javaDigit})";
    String groupedNumeral = "(" + "[\\p{javaDigit}&&[^0]]" + digit + "?" + digit + "?(" +
            "\\," + digit + digit + digit + ")+)";
    String numeral = "((" + digit + "++)|" + groupedNumeral + ")";
    String javaStyleInteger = "([-+]?(" + numeral + "))";
    String negativeInteger = "\\-" + numeral + "";
    String positiveInteger = "" + numeral + "";
    return "(" + javaStyleInteger + ")|(" +
            positiveInteger + ")|(" +
            negativeInteger + ")";
}

我很确定你可以用谷歌搜索所有的正则表达式或在这里找到它们。

但是,如果有一个“更轻松”的解决方案,或者您根本不想使用正则表达式方法怎么办?

您可以通过引入Map&lt;String, Predicate&lt;String&gt;&gt; 来混合不同的技术:

class Main {

    private final Map<String, Predicate<String>> predicates = Map.of(
            "Integer", this::isInteger,
            "JSON", this::isJSON
    );

    public String getDataType(String input) {
        for (Map.Entry<String, Predicate<String>> predicate : predicates.entrySet()) {
            if (predicate.getValue().test(input)) {
                return predicate.getKey();
            }
        }
        return "Unknown";
    }

    private boolean isJSON(String input) {
        return LibraryClass.isJSON(input);
    }

    private boolean isInteger(String input) {
        try {
            Integer.valueOf(input);
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }

}

我认为它更具可读性,它降低了嵌套级别并将不同的逻辑封装在不同的方法中。

【讨论】:

  • 我看到一个关于 SO 的问题是使用正则表达式来确定 IntegerDouble
  • 正则表达式总是一种“更好”的确定方法,但不会产生与上述相同的确切结果,并且在许多情况下可能会更慢 - 我认为值得澄清这一点,因为最初的问题集中在关于性能。
【解决方案3】:

它的长短是:

  • 没有明智的方法来封装这个确切的功能,同时又不捕获这些异常。
  • 如果您正在谈论将该方法运行“数百次”,那么它确实不应该成为性能瓶颈。如果是,则对其进行基准测试,找出该方法中的瓶颈所在,然后具体解决这些问题。

不过,为了完成,您可以在不捕获这些异常的情况下完成类似的事情 - 但请记住,我没有做过基准测试,这些结果可能会更快,也可能不会更快你的情况!

用于判断某些内容是否为 JSON,there's a mayBeJSON() method in JSON-lib,但它是一个相当过时的库(最后更新于 2010 年。)

如果您想知道某些东西是否适合 int,那么除了真正将 NFE 赶出 parseInt() 之外,没有其他明智的方法。但是,如果您只想知道某物是否是数字,则可以使用正则表达式:

str.matches("\\d+"); //Returns true if it's a whole positive number
str.matches("-?\\d+"); //Returns true if it's a whole number

(其他正则表达式很容易计算/在线找到十进制数、带指数的数字等)

或者您可以流式传输,并单独检查每个字符:

str.chars().allMatch(c->Character.isDigit(c));

或者,您可能希望使用来自 Apache Commons 的 NumberUtils.isCreateable() 预先或替换您的异常检查(isParseable(),该类上的其他方法也可能有用。)

【讨论】:

  • 我试图查看source code 以实现isParseable() 方法,但我找不到它,现在是否已弃用?
  • 该函数的 javadoc 确实说它不会解析十六进制值,但是如果我们传递 2.4f 这是可解析的,但由于 Character.isDigit() 条件检查,它将返回 false,这是'不可取。
  • @Kainix 是的,它根本不是一个完美的替代品——这完全取决于你想要达到什么目标,什么对你的用例有用/不适用。
【解决方案4】:

我想知道输入是否是json/jsonarray。

您需要处理JSONException来确定JSON类型,然后您可以使用NumberFormat parse()方法来确定其他类型,如下所示:

    private static String getDataType(String input) {
       try {
            return getJsonObjectType(input);
        } catch (JSONException ex1) {
            try {
                return getJsonArrayType(input);
            } catch (JSONException ex2) {
                   return getNonJsonType(input);
           }
       }
    }

    private static String getJsonArrayType(String input) throws JSONException {
        new JSONArray(input);
        return "JSONArray";
    }

    private static String getJsonObjectType(String input) throws JSONException {
        new JSONObject((String) input);
        return "JSONObject";
    }

    private static String getNonJsonType(String input) {
        try {
             return getNumberType(input);
         } catch(ParseException ex3) {
             return "String";
         }
    }

    private static String getNumberType(String input) throws ParseException {
         Number number = NumberFormat.getInstance().parse(input);
         if(number instanceof Long) {
             if(number.longValue() < Integer.MAX_VALUE) {
                 return "int";
             } else {
                 return "long";
             }
         } else {
             if(number.doubleValue() < Float.MAX_VALUE) {
                 return "float";
             } else {
                 return "double";
             }
         }
    }

【讨论】:

  • 我使用JSON是因为我想知道它是否是一个json/jsonarray。
  • 您的代码可能会通过返回“float”来丢弃double 的精度。考虑一个诸如 3.1415926536 之类的输入,即使浮点数不能保持那么高的精度,它也会返回“float”。你最好总是返回double
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多