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




Строки - это неизменяемые типы. Вы не можете их изменять, только возвращайте новые экземпляры строк.
Из-за этого форматирование с помощью метода экземпляра не имеет особого смысла, так как его нужно было бы вызывать так:
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», мы просто проверяем его и возвращаем его длину. Это идеальный кандидат для метода экземпляра.
Мои простые правила для методов экземпляра на неизменяемых объектах:
Поскольку строка формата обычно больше похожа на «Цена% 4d», а не на «% 4d», я все же вижу много возможностей для путаницы. Что вы имеете против статических методов? :)
Этот ответ, кажется, не имеет ничего общего с вопросом.
Ответ даже не на Java, кажется, более актуален для .NET.
-1. Проголосовали против, так как это касательное. И не обязательно лучший стиль для неизменяемых. Этот стиль более подробен, чем простые вызовы методов, особенно для связанных операций. И он отказывается от полиморфизма во время выполнения, потому что он вызывает статические методы, что важно. YMMV.
Следуя вашей логике. "ABC".toLowerCase() тоже был бы неправ.
Объясняя, почему предполагаемый String.Format должен быть статическим методом, а не методом экземпляра, ничего отвечает на вопрос «что эквивалент sprintf в Java?». (Ответ: "String.format, это статический метод", кстати.)
Этот API предварительно был добавлен в Java 13. Например, теперь поддерживается String str = "%s: %d".formatted(name, val);. Это будет особенно полезно для формирования сообщений об исключениях.
Оба решения работают для имитации 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)));
...
... но это было бы В самом деле некрасиво.
Все отличные идеи, но превращает ваш код в код, предназначенный только для записи, который невозможно понять вашему коллеге.
Вы можете сделать 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();
Я вижу, что вы каким-то образом поняли, что я предлагаю изменить строку формата. Я никогда не рассматривал возможность того, что кто-то может ожидать изменения String, поскольку их неизменность является фундаментальной.