在JAVA中动态确定用户输入数据类型

时间:2018-02-08 10:23:55

标签: java json

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

更新:已删除解析为Float,因为Double值也可以按照@DodgyCodeException提及的某些精度解析为Float

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这是一项昂贵的操作。
有没有更好的方法来实现这个目标?

4 个答案:

答案 0 :(得分:7)

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

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(),以获得与现有功能几乎完全相同的功能。

答案 1 :(得分:5)

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

它与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<String, Predicate<String>>

来混合使用不同的技巧
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;
    }

}

我相信它更具可读性,它会降低嵌套级别,并在不同的方法中封装不同的逻辑。

答案 2 :(得分:1)

  

我想知道输入是否是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";
             }
         }
    }

答案 3 :(得分:1)

它的长短是:

  • 在没有捕获这些例外情况的情况下,没有合理的方法来封装这些确切的功能。
  • 如果您正在谈论运行该方法&#34;数百&#34;有时,它确实不应该成为性能瓶颈。如果是,那么对它进行基准测试,找出该方法中瓶颈的位置,然后专门解决这些问题。

为了完成,你可以完成类似的事情而不会发现这些例外情况 - 尽管请记住我没有做过基准测试,并且这些结果可能会或者你的情况可能不会更快!

如果想知道某些东西的JSON,there's a mayBeJSON() method in JSON-lib,但它是一个相当过时的图书馆(最近更新的2010年)。

如果你想知道某些东西是否适合某个特定的int,那么除了parseInt()真正关闭NFE之外别无其他明智的方法。但是,如果您只想知道某些内容是否为数字,则可以使用正则表达式:

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(isParseable())中使用NumberUtils.isCreateable()作为前缀或替换您的异常检查,并且该类上的其他方法也可能有用。)