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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
288
0
198 338
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

// Store the formatted string in 'result'
String result = String.format("%4d", i * j);

// Write the result to standard output
System.out.println( result );

См. формат и его синтаксис

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

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

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», мы просто проверяем его и возвращаем его длину. Это идеальный кандидат для метода экземпляра.

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

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

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

erickson 13.10.2008 22:14

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

FlySwat 13.10.2008 22:20

Этот ответ, кажется, не имеет ничего общего с вопросом.

Steve McLeod 06.06.2009 11:58

Ответ даже не на Java, кажется, более актуален для .NET.

Photodeus 19.11.2009 09:35

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

Andrew Janke 21.04.2014 07:11

Следуя вашей логике. "ABC".toLowerCase() тоже был бы неправ.

glglgl 07.07.2016 14:23

Объясняя, почему предполагаемый String.Format должен быть статическим методом, а не методом экземпляра, ничего отвечает на вопрос «что эквивалент sprintf в Java?». (Ответ: "String.format, это статический метод", кстати.)

Martin Bonner supports Monica 28.03.2017 17:46

Этот API предварительно был добавлен в Java 13. Например, теперь поддерживается String str = "%s: %d".formatted(name, val);. Это будет особенно полезно для формирования сообщений об исключениях.

erickson 20.12.2019 23:20

Оба решения работают для имитации 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)));
    ...
    

... но это было бы В самом деле некрасиво.

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

Ben 14.07.2017 12:01

Вы можете сделать 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();

Начиная с Java 13 у вас есть метод formatted1 для String, который был добавлен вместе с текстовыми блоками в качестве функции предварительного просмотра 2. Можно использовать вместо String.format()

Assertions.assertEquals(
   "%s %d %.3f".formatted("foo", 123, 7.89),
   "foo 123 7.890"
);

Другие вопросы по теме