有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何在java中将1200格式化为1.2k

我想用java将以下数字格式化为它们旁边的数字:

1000 to 1k
5821 to 5.8k
10500 to 10k
101800 to 101k
2000000 to 2m
7800000 to 7.8m
92150000 to 92m
123200000 to 123m

右边的数字为长或整数,左边的数字为字符串。 我应该如何处理这个问题。我已经为此做了一点算法,但我认为可能已经有一些发明出来了,在这方面做得更好,如果我开始处理数十亿和万亿的数据,就不需要额外的测试:)

其他要求:

  • 格式最多应包含4个字符
  • 以上表示1.1k正常,11.2k不正常。7.8米也可以,19.1米不行。小数点前一位只能有小数点。小数点之前的两位数表示小数点之后的数字
  • 不需要四舍五入。(附加k和m的数字更多的是模拟仪表,表示近似值,而不是精确的逻辑条款。因此舍入是不相关的,这主要是因为变量的性质,即使在查看缓存结果时,舍入也可以增加或减少几个数字。)

共 (6) 个答案

  1. # 1 楼答案

    这是一个适用于任何长值的解决方案,我发现它非常可读(核心逻辑在format方法的底部三行完成)

    它利用TreeMap找到合适的后缀。令人惊讶的是,它比我以前编写的使用数组的解决方案更高效,而且更难阅读

    private static final NavigableMap<Long, String> suffixes = new TreeMap<> ();
    static {
      suffixes.put(1_000L, "k");
      suffixes.put(1_000_000L, "M");
      suffixes.put(1_000_000_000L, "G");
      suffixes.put(1_000_000_000_000L, "T");
      suffixes.put(1_000_000_000_000_000L, "P");
      suffixes.put(1_000_000_000_000_000_000L, "E");
    }
    
    public static String format(long value) {
      //Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
      if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1);
      if (value < 0) return "-" + format(-value);
      if (value < 1000) return Long.toString(value); //deal with easy case
    
      Entry<Long, String> e = suffixes.floorEntry(value);
      Long divideBy = e.getKey();
      String suffix = e.getValue();
    
      long truncated = value / (divideBy / 10); //the number part of the output times 10
      boolean hasDecimal = truncated < 100 && (truncated / 10d) != (truncated / 10);
      return hasDecimal ? (truncated / 10d) + suffix : (truncated / 10) + suffix;
    }
    

    测试代码

    public static void main(String args[]) {
      long[] numbers = {0, 5, 999, 1_000, -5_821, 10_500, -101_800, 2_000_000, -7_800_000, 92_150_000, 123_200_000, 9_999_999, 999_999_999_999_999_999L, 1_230_000_000_000_000L, Long.MIN_VALUE, Long.MAX_VALUE};
      String[] expected = {"0", "5", "999", "1k", "-5.8k", "10k", "-101k", "2M", "-7.8M", "92M", "123M", "9.9M", "999P", "1.2P", "-9.2E", "9.2E"};
      for (int i = 0; i < numbers.length; i++) {
        long n = numbers[i];
        String formatted = format(n);
        System.out.println(n + " => " + formatted);
        if (!formatted.equals(expected[i])) throw new AssertionError("Expected: " + expected[i] + " but found: " + formatted);
      }
    }
    
  2. # 2 楼答案

    当前答案的问题

    • 当前的许多解决方案都使用这些前缀k=103,m=106,b=109,t=1012。然而,根据{a1}{a2},正确的前缀是k=103,M=106,G=109,T=1012
    • 缺乏对负数的支持(或至少缺乏证明负数得到支持的测试)
    • 缺乏对反向操作的支持,例如将1.1k转换为1100(尽管这超出了原始问题的范围)

    Java解决方案

    这个解决方案(对this answer的扩展)解决了上述问题

    import org.apache.commons.lang.math.NumberUtils;
    
    import java.text.DecimalFormat;
    import java.text.FieldPosition;
    import java.text.Format;
    import java.text.ParsePosition;
    import java.util.regex.Pattern;
    
    
    /**
     * Converts a number to a string in <a href="http://en.wikipedia.org/wiki/Metric_prefix">metric prefix</a> format.
     * For example, 7800000 will be formatted as '7.8M'. Numbers under 1000 will be unchanged. Refer to the tests for further examples.
     */
    class RoundedMetricPrefixFormat extends Format {
    
        private static final String[] METRIC_PREFIXES = new String[]{"", "k", "M", "G", "T"};
    
        /**
         * The maximum number of characters in the output, excluding the negative sign
         */
        private static final Integer MAX_LENGTH = 4;
    
        private static final Pattern TRAILING_DECIMAL_POINT = Pattern.compile("[0-9]+\\.[kMGT]");
    
        private static final Pattern METRIC_PREFIXED_NUMBER = Pattern.compile("\\-?[0-9]+(\\.[0-9])?[kMGT]");
    
        @Override
        public StringBuffer format(Object obj, StringBuffer output, FieldPosition pos) {
    
            Double number = Double.valueOf(obj.toString());
    
            // if the number is negative, convert it to a positive number and add the minus sign to the output at the end
            boolean isNegative = number < 0;
            number = Math.abs(number);
    
            String result = new DecimalFormat("##0E0").format(number);
    
            Integer index = Character.getNumericValue(result.charAt(result.length() - 1)) / 3;
            result = result.replaceAll("E[0-9]", METRIC_PREFIXES[index]);
    
            while (result.length() > MAX_LENGTH || TRAILING_DECIMAL_POINT.matcher(result).matches()) {
                int length = result.length();
                result = result.substring(0, length - 2) + result.substring(length - 1);
            }
    
            return output.append(isNegative ? "-" + result : result);
        }
    
        /**
         * Convert a String produced by <tt>format()</tt> back to a number. This will generally not restore
         * the original number because <tt>format()</tt> is a lossy operation, e.g.
         *
         * <pre>
         * {@code
         * def formatter = new RoundedMetricPrefixFormat()
         * Long number = 5821L
         * String formattedNumber = formatter.format(number)
         * assert formattedNumber == '5.8k'
         *
         * Long parsedNumber = formatter.parseObject(formattedNumber)
         * assert parsedNumber == 5800
         * assert parsedNumber != number
         * }
         * </pre>
         *
         * @param source a number that may have a metric prefix
         * @param pos if parsing succeeds, this should be updated to the index after the last parsed character
         * @return a Number if the the string is a number without a metric prefix, or a Long if it has a metric prefix
         */
        @Override
        public Object parseObject(String source, ParsePosition pos) {
    
            if (NumberUtils.isNumber(source)) {
    
                // if the value is a number (without a prefix) don't return it as a Long or we'll lose any decimals
                pos.setIndex(source.length());
                return toNumber(source);
    
            } else if (METRIC_PREFIXED_NUMBER.matcher(source).matches()) {
    
                boolean isNegative = source.charAt(0) == '-';
                int length = source.length();
    
                String number = isNegative ? source.substring(1, length - 1) : source.substring(0, length - 1);
                String metricPrefix = Character.toString(source.charAt(length - 1));
    
                Number absoluteNumber = toNumber(number);
    
                int index = 0;
    
                for (; index < METRIC_PREFIXES.length; index++) {
                    if (METRIC_PREFIXES[index].equals(metricPrefix)) {
                        break;
                    }
                }
    
                Integer exponent = 3 * index;
                Double factor = Math.pow(10, exponent);
                factor *= isNegative ? -1 : 1;
    
                pos.setIndex(source.length());
                Float result = absoluteNumber.floatValue() * factor.longValue();
                return result.longValue();
            }
    
            return null;
        }
    
        private static Number toNumber(String number) {
            return NumberUtils.createNumber(number);
        }
    }
    

    常规解决方案

    该解决方案最初是用Groovy编写的,如下所示

    import org.apache.commons.lang.math.NumberUtils
    
    import java.text.DecimalFormat
    import java.text.FieldPosition
    import java.text.Format
    import java.text.ParsePosition
    import java.util.regex.Pattern
    
    
    /**
     * Converts a number to a string in <a href="http://en.wikipedia.org/wiki/Metric_prefix">metric prefix</a> format.
     * For example, 7800000 will be formatted as '7.8M'. Numbers under 1000 will be unchanged. Refer to the tests for further examples.
     */
    class RoundedMetricPrefixFormat extends Format {
    
        private static final METRIC_PREFIXES = ["", "k", "M", "G", "T"]
    
        /**
         * The maximum number of characters in the output, excluding the negative sign
         */
        private static final Integer MAX_LENGTH = 4
    
        private static final Pattern TRAILING_DECIMAL_POINT = ~/[0-9]+\.[kMGT]/
    
        private static final Pattern METRIC_PREFIXED_NUMBER = ~/\-?[0-9]+(\.[0-9])?[kMGT]/
    
        @Override
        StringBuffer format(Object obj, StringBuffer output, FieldPosition pos) {
    
            Double number = obj as Double
    
            // if the number is negative, convert it to a positive number and add the minus sign to the output at the end
            boolean isNegative = number < 0
            number = Math.abs(number)
    
            String result = new DecimalFormat("##0E0").format(number)
    
            Integer index = Character.getNumericValue(result.charAt(result.size() - 1)) / 3
            result = result.replaceAll("E[0-9]", METRIC_PREFIXES[index])
    
            while (result.size() > MAX_LENGTH || TRAILING_DECIMAL_POINT.matcher(result).matches()) {
                int length = result.size()
                result = result.substring(0, length - 2) + result.substring(length - 1)
            }
    
            output << (isNegative ? "-$result" : result)
        }
    
        /**
         * Convert a String produced by <tt>format()</tt> back to a number. This will generally not restore
         * the original number because <tt>format()</tt> is a lossy operation, e.g.
         *
         * <pre>
         * {@code
         * def formatter = new RoundedMetricPrefixFormat()
         * Long number = 5821L
         * String formattedNumber = formatter.format(number)
         * assert formattedNumber == '5.8k'
         *
         * Long parsedNumber = formatter.parseObject(formattedNumber)
         * assert parsedNumber == 5800
         * assert parsedNumber != number
         * }
         * </pre>
         *
         * @param source a number that may have a metric prefix
         * @param pos if parsing succeeds, this should be updated to the index after the last parsed character
         * @return a Number if the the string is a number without a metric prefix, or a Long if it has a metric prefix
         */
        @Override
        Object parseObject(String source, ParsePosition pos) {
    
            if (source.isNumber()) {
    
                // if the value is a number (without a prefix) don't return it as a Long or we'll lose any decimals
                pos.index = source.size()
                toNumber(source)
    
            } else if (METRIC_PREFIXED_NUMBER.matcher(source).matches()) {
    
                boolean isNegative = source[0] == '-'
    
                String number = isNegative ? source[1..-2] : source[0..-2]
                String metricPrefix = source[-1]
    
                Number absoluteNumber = toNumber(number)
    
                Integer exponent = 3 * METRIC_PREFIXES.indexOf(metricPrefix)
                Long factor = 10 ** exponent
                factor *= isNegative ? -1 : 1
    
                pos.index = source.size()
                (absoluteNumber * factor) as Long
            }
        }
    
        private static Number toNumber(String number) {
            NumberUtils.createNumber(number)
        }
    }
    

    测试(Groovy)

    这些测试是用Groovy编写的,但可以用来验证Java或Groovy类(因为它们都有相同的名称和API)

    import java.text.Format
    import java.text.ParseException
    
    class RoundedMetricPrefixFormatTests extends GroovyTestCase {
    
        private Format roundedMetricPrefixFormat = new RoundedMetricPrefixFormat()
    
        void testNumberFormatting() {
    
            [
                    7L         : '7',
                    12L        : '12',
                    856L       : '856',
                    1000L      : '1k',
                    (-1000L)   : '-1k',
                    5821L      : '5.8k',
                    10500L     : '10k',
                    101800L    : '102k',
                    2000000L   : '2M',
                    7800000L   : '7.8M',
                    (-7800000L): '-7.8M',
                    92150000L  : '92M',
                    123200000L : '123M',
                    9999999L   : '10M',
                    (-9999999L): '-10M'
            ].each { Long rawValue, String expectedRoundValue ->
    
                assertEquals expectedRoundValue, roundedMetricPrefixFormat.format(rawValue)
            }
        }
    
        void testStringParsingSuccess() {
            [
                    '7'    : 7,
                    '8.2'  : 8.2F,
                    '856'  : 856,
                    '-856' : -856,
                    '1k'   : 1000,
                    '5.8k' : 5800,
                    '-5.8k': -5800,
                    '10k'  : 10000,
                    '102k' : 102000,
                    '2M'   : 2000000,
                    '7.8M' : 7800000L,
                    '92M'  : 92000000L,
                    '-92M' : -92000000L,
                    '123M' : 123000000L,
                    '10M'  : 10000000L
    
            ].each { String metricPrefixNumber, Number expectedValue ->
    
                def parsedNumber = roundedMetricPrefixFormat.parseObject(metricPrefixNumber)
                assertEquals expectedValue, parsedNumber
            }
        }
    
        void testStringParsingFail() {
    
            shouldFail(ParseException) {
                roundedMetricPrefixFormat.parseObject('notNumber')
            }
        }
    }
    
  3. # 3 楼答案

    这里有一个利用DecimalFormat的工程符号的解决方案:

    public static void main(String args[]) {
        long[] numbers = new long[]{7, 12, 856, 1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
        for(long number : numbers) {
            System.out.println(number + " = " + format(number));
        }
    }
    
    private static String[] suffix = new String[]{"","k", "m", "b", "t"};
    private static int MAX_LENGTH = 4;
    
    private static String format(double number) {
        String r = new DecimalFormat("##0E0").format(number);
        r = r.replaceAll("E[0-9]", suffix[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]);
        while(r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")){
            r = r.substring(0, r.length()-2) + r.substring(r.length() - 1);
        }
        return r;
    }
    

    输出:

    7 = 7
    12 = 12
    856 = 856
    1000 = 1k
    5821 = 5.8k
    10500 = 10k
    101800 = 102k
    2000000 = 2m
    7800000 = 7.8m
    92150000 = 92m
    123200000 = 123m
    9999999 = 10m
    
  4. # 4 楼答案

    我的功能转换大到小的数字(2位)。您可以通过在DecimalFormat中更改#.##来更改位数

    public String formatValue(float value) {
        String arr[] = {"", "K", "M", "B", "T", "P", "E"};
        int index = 0;
        while ((value / 1000) >= 1) {
            value = value / 1000;
            index++;
        }
        DecimalFormat decimalFormat = new DecimalFormat("#.##");
        return String.format("%s %s", decimalFormat.format(value), arr[index]);
    }
    

    测试

    System.out.println(formatValue(100));     //  100
    System.out.println(formatValue(1000));    // 1 K
    System.out.println(formatValue(10345));   // 10.35 K
    System.out.println(formatValue(10012));   // 10.01 K
    System.out.println(formatValue(123456));  // 123.46 K
    System.out.println(formatValue(4384324)); // 4.38 M
    System.out.println(formatValue(10000000)); // 10 M
    System.out.println(formatValue(Long.MAX_VALUE)); // 9.22 E
    

    希望能有所帮助

  5. # 5 楼答案

    需要一些改进,但是:快来营救吧
    你可以把后缀放在一个字符串或数组中,然后根据功率或者类似的东西获取它们
    部门也可以围绕权力进行管理,我认为几乎所有事情都与权力价值有关。 希望有帮助

    public static String formatValue(double value) {
    int power; 
        String suffix = " kmbt";
        String formattedNumber = "";
    
        NumberFormat formatter = new DecimalFormat("#,###.#");
        power = (int)StrictMath.log10(value);
        value = value/(Math.pow(10,(power/3)*3));
        formattedNumber=formatter.format(value);
        formattedNumber = formattedNumber + suffix.charAt(power/3);
        return formattedNumber.length()>4 ?  formattedNumber.replaceAll("\\.[0-9]+", "") : formattedNumber;  
    }
    

    产出:

    999
    1.2k
    98k
    911k
    1.1m
    11b
    712b
    34t

  6. # 6 楼答案

    我知道,这看起来更像一个C程序,但它是超轻量的

    public static void main(String args[]) {
        long[] numbers = new long[]{1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
        for(long n : numbers) {
            System.out.println(n + " => " + coolFormat(n, 0));
        }
    }
    
    private static char[] c = new char[]{'k', 'm', 'b', 't'};
    
    /**
     * Recursive implementation, invokes itself for each factor of a thousand, increasing the class on each invokation.
     * @param n the number to format
     * @param iteration in fact this is the class from the array c
     * @return a String representing the number n formatted in a cool looking way.
     */
    private static String coolFormat(double n, int iteration) {
        double d = ((long) n / 100) / 10.0;
        boolean isRound = (d * 10) %10 == 0;//true if the decimal part is equal to 0 (then it's trimmed anyway)
        return (d < 1000? //this determines the class, i.e. 'k', 'm' etc
            ((d > 99.9 || isRound || (!isRound && d > 9.99)? //this decides whether to trim the decimals
             (int) d * 10 / 10 : d + "" // (int) d * 10 / 10 drops the decimal
             ) + "" + c[iteration]) 
            : coolFormat(d, iteration+1));
    
    }
    

    它输出:

    1000 => 1k
    5821 => 5.8k
    10500 => 10k
    101800 => 101k
    2000000 => 2m
    7800000 => 7.8m
    92150000 => 92m
    123200000 => 123m
    9999999 => 9.9m