Эквивалент Sprintf в Java


284

Printf был добавлен в Java с выпуском 1.5, но я не могу найти, как отправить вывод в строку, а не в файл (что делает sprintf в C). Кто-нибудь знает как это сделать?

Ответы:



28

Строки являются неизменяемыми типами. Вы не можете изменять их, только возвращать новые экземпляры строк.

Из-за этого форматирование с помощью метода экземпляра не имеет большого смысла, так как его нужно вызывать так:

String formatted = "%s: %s".format(key, value);

Первоначальные авторы Java (и авторы .NET) решили, что статический метод имеет больше смысла в этой ситуации, поскольку вы не изменяете цель, а вместо этого вызываете метод форматирования и передаете входную строку.

Вот пример того, почему format()был бы глуп как метод экземпляра. В .NET (и, вероятно, в Java), Replace()это метод экземпляра.

Ты можешь сделать это:

 "I Like Wine".Replace("Wine","Beer");

Однако ничего не происходит, потому что строки неизменяемы. Replace()пытается вернуть новую строку, но она ничего не назначается.

Это вызывает много распространенных ошибок новичка, таких как:

inputText.Replace(" ", "%20");

Опять же, ничего не происходит, вместо этого вы должны сделать:

inputText = inputText.Replace(" ","%20");

Теперь, если вы понимаете, что строки неизменны, это имеет смысл. Если вы этого не сделаете, то вы просто запутались. Правильное место для Replace()будет где format(), как статический метод String:

 inputText = String.Replace(inputText, " ", "%20");

Теперь нет никаких сомнений относительно того, что происходит.

Реальный вопрос заключается в том, почему авторы этих платформ решили, что один должен быть методом экземпляра, а другой - статичным? На мой взгляд, оба более элегантно выражены как статические методы.

Независимо от вашего мнения, правда заключается в том, что вы менее склонны совершать ошибки, используя статическую версию, а код легче понять (без скрытых ошибок).

Конечно, есть некоторые методы, которые идеально подходят как методы экземпляра, возьмите String.Length ()

int length = "123".Length();

В этой ситуации очевидно, что мы не пытаемся изменить «123», мы просто проверяем его и возвращаем его длину. Это идеальный кандидат для метода экземпляра.

Мои простые правила для методов экземпляров на неизменяемых объектах:

  • Если вам нужно вернуть новый экземпляр того же типа, используйте статический метод.
  • В противном случае используйте метод экземпляра.

4
Я вижу, что вы почему-то поняли, что я предлагал изменить форматную строку. Я никогда не думал о том, что кто-то может ожидать, что строка изменится, так как их неизменность настолько фундаментальна.
Эриксон

4
Учитывая, что строка формата обычно больше похожа на «Цена% 4d», а не на «% 4d», я все еще вижу большой потенциал для путаницы. Что вы имеете против статических методов? :)
FlySwat

44
Этот ответ, похоже, не имеет ничего общего с вопросом.
Стив Маклеод

2
Ответ даже не Java, кажется, больше относится к .NET
Photodeus

3
-1. Понижено б / к это тангенциально. И не обязательно лучший стиль для неизменяемых. Этот стиль более многословен, чем простые вызовы методов, особенно для связанных операций. И он отказывается от полиморфизма во время выполнения, потому что вызывает статические методы, что очень важно. YMMV.
Эндрю Янке

3

Оба решения работают для имитации printf, но по-разному. Например, чтобы преобразовать значение в шестнадцатеричную строку, у вас есть 2 следующих решения:

  • с format(), ближайший к sprintf():

    final static String HexChars = "0123456789abcdef";
    
    public static String getHexQuad(long v) {
        String ret;
        if(v > 0xffff) ret = getHexQuad(v >> 16); else ret = "";
        ret += String.format("%c%c%c%c",
            HexChars.charAt((int) ((v >> 12) & 0x0f)),
            HexChars.charAt((int) ((v >>  8) & 0x0f)),
            HexChars.charAt((int) ((v >>  4) & 0x0f)),
            HexChars.charAt((int) ( v        & 0x0f)));
        return ret;
    }
  • с replace(char oldchar , char newchar), немного быстрее, но довольно ограничено:

        ...
        ret += "ABCD".
            replace('A', HexChars.charAt((int) ((v >> 12) & 0x0f))).
            replace('B', HexChars.charAt((int) ((v >>  8) & 0x0f))).
            replace('C', HexChars.charAt((int) ((v >>  4) & 0x0f))).
            replace('D', HexChars.charAt((int) ( v        & 0x0f)));
        ...
  • Существует третье решение, состоящее в том, чтобы просто добавлять символ retодин за другим (символы - это числа, которые дополняют друг друга !), Например:

    ...
    ret += HexChars.charAt((int) ((v >> 12) & 0x0f)));
    ret += HexChars.charAt((int) ((v >>  8) & 0x0f)));
    ...

... но это было бы действительно ужасно


Все отличные идеи, но превращают ваш код в код только для записи, который невозможно понять вашему коллеге.
Бен

0

Вы можете сделать printf для всего, что является OutputStream с PrintStream. Как-то так, печать в поток строки:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s", "hello", 3, "friends");
System.out.println(baos.toString());
baos.reset(); //need reset to write new string
ps.printf("there is a %s from %d %s", "flip", 5, "haters");
System.out.println(baos.toString());
baos.reset();

Поток строки может быть создан как этот ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.