Ответ 1
ОБНОВЛЕНИЕ: Пожалуйста, смотрите (и проголосуйте) за сообщение Джозефа Люста ниже, прежде чем смотреть дальше на этот ответ.
Похоже, что форматирование не включается в соответствии с этот пост. Однако они предлагают несколько альтернатив.
Как мне форматировать мою строку в GWT?
Я сделал метод
Formatter format = new Formatter();
int matches = 0;
Formatter formattedString = format.format("%d numbers(s, args) in correct position", matches);
return formattedString.toString();
Но он жалуется, говоря
Validating newly compiled units
[ERROR] Errors in 'file:/C:/Documents%20and%20Settings/kkshetri/workspace/MasterMind/MasterMind/src/com/kunjan/MasterMind/client/MasterMind.java'
[ERROR] Line 84: No source code is available for type java.util.Formatter; did you forget to inherit a required module?
Не включен ли Formatter?
ОБНОВЛЕНИЕ: Пожалуйста, смотрите (и проголосуйте) за сообщение Джозефа Люста ниже, прежде чем смотреть дальше на этот ответ.
Похоже, что форматирование не включается в соответствии с этот пост. Однако они предлагают несколько альтернатив.
См. официальную страницу в формате даты и номера GWT.
Они предлагают следующее:
myNum decimal = 33.23232;
myString = NumberFormat.getFormat("#.00").format(decimal);
Лучше всего использовать их поддерживаемые оптимизированные методы, чем готовить свой собственный неоптимальный метод. Их компилятор будет оптимизировать их всех почти так же, как и в конце концов.
Очень простая замена для String.format() в GWT 2.1 +:
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.regexp.shared.SplitResult;
public static String format(final String format, final Object... args) {
final RegExp regex = RegExp.compile("%[a-z]");
final SplitResult split = regex.split(format);
final StringBuffer msg = new StringBuffer();
for (int pos = 0; pos < split.length() - 1; ++pos) {
msg.append(split.get(pos));
msg.append(args[pos].toString());
}
msg.append(split.get(split.length() - 1));
return msg.toString();
}
Или даже проще, не используя RegExp, и используя только строки:
public static String format(final String format, final String... args) {
String[] split = format.split("%s");
final StringBuffer msg = new StringBuffer();
for (int pos = 0; pos < split.length - 1; pos += 1) {
msg.append(split[pos]);
msg.append(args[pos]);
}
msg.append(split[split.length - 1]);
return msg.toString();
}
Другое предложение, в котором используется JSNI и хорошая функция формата JavaScript из другого сообщение:
import com.google.gwt.core.client.JsArrayString;
public abstract class StringFormatter {
public static String format(final String format, final Object... args) {
if (null == args || 0 == args.length)
return format;
JsArrayString array = newArray();
for (Object arg : args) {
array.push(String.valueOf(arg)); // TODO: smarter conversion?
}
return nativeFormat(format, array);
}
private static native JsArrayString newArray()/*-{
return [];
}-*/;
private static native String nativeFormat(final String format, final JsArrayString args)/*-{
return format.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
}-*/;
}
Затем можно сделать вызов следующим образом:
StringFormatter.format("Greetings {0}, it {1} o'clock, which is a {2} statement", "Master", 8, false);
... с результатом
Приветствие Мастер, это 8 часов, что является ложным утверждением
Существует потенциал для дальнейшего улучшения в комментарии TODO, например. используйте NumberFormat. Предложения приветствуются.
Это довольно быстро и игнорирует плохие значения, зависящие от курсива:
public static String format(final String format, final Object... args)
{
if (format == null || format.isEmpty()) return "";
// Approximate the result length: format string + 16 character args
StringBuilder sb = new StringBuilder(format.length() + (args.length*16));
final char openDelim = '{';
final char closeDelim = '}';
int cur = 0;
int len = format.length();
int open;
int close;
while (cur < len)
{
switch (open = format.indexOf(openDelim, cur))
{
case -1:
return sb.append(format.substring(cur, len)).toString();
default:
sb.append(format.substring(cur, open));
switch (close = format.indexOf(closeDelim, open))
{
case -1:
return sb.append(format.substring(open)).toString();
default:
String nStr = format.substring(open + 1, close);
try
{
// Append the corresponding argument value
sb.append(args[Integer.parseInt(nStr)]);
}
catch (Exception e)
{
// Append the curlies and the original delimited value
sb.append(openDelim).append(nStr).append(closeDelim);
}
cur = close + 1;
}
}
}
return sb.toString();
}
Я не хочу злоупотреблять строковыми манипуляциями для выполнения задания регулярных выражений, но, основываясь на решении bodrin, вы можете закодировать:
public static String format (String pattern, final Object ... args) {
for (Object arg : args) {
String part1 = pattern.substring(0,pattern.indexOf('{'));
String part2 = pattern.substring(pattern.indexOf('}') + 1);
pattern = part1 + arg + part2;
}
return pattern;
}
Расширение для решения Daniels: также поддерживает экранирование с помощью 'и throws, если число не может быть проанализировано (например, версия JVM):
private static final char OPEN = '{';
private static final char CLOSE = '}';
private static final char ESCAPE = '\'';
@Override
public String format(String pattern, Object... arguments) {
if (pattern == null || pattern.isEmpty())
return "";
// Approximate the result length: format string + 16 character args
StringBuilder sb = new StringBuilder(pattern.length() + (arguments.length * 16));
int cur = 0;
int len = pattern.length();
// if escaped, then its >= 0
int escapedAtIndex = -1;
while (cur < len) {
char currentChar = pattern.charAt(cur);
switch (currentChar) {
case OPEN:
if (escapedAtIndex >= 0) {
// currently escaped
sb.append(currentChar);
} else {
// find close
int close = pattern.indexOf(CLOSE, cur + 1);
switch (close) {
case -1:
// Missing close. Actually an error. But just ignore
sb.append(currentChar);
break;
default:
// Ok, we have a close
final String nStr = pattern.substring(cur + 1, close);
try {
// Append the corresponding argument value
sb.append(arguments[Integer.parseInt(nStr)]);
} catch (Exception e) {
if (e instanceof NumberFormatException) {
throw new IllegalArgumentException(nStr +
" is not a number.");
}
// Append the curlies and the original delimited value
sb.append(OPEN).append(nStr).append(CLOSE);
}
// Continue after the close
cur = close;
break;
}
}
cur++;
break;
case ESCAPE:
// Special case: two '' are just converted to '
boolean nextIsEscapeToo = (cur + 1 < len) && pattern.charAt(cur + 1) == ESCAPE;
if (nextIsEscapeToo) {
sb.append(ESCAPE);
cur = cur + 2;
} else {
if (escapedAtIndex >= 0) {
// Escape end.
escapedAtIndex = -1;
} else {
// Escape start.
escapedAtIndex = cur;
}
cur++;
}
break;
default:
// 90% case: Nothing special, just a normal character
sb.append(currentChar);
cur++;
break;
}
}
return sb.toString();
}
Эта реализация и JVM-версия проходят эти тесты:
// Replace: 0 items
assertFormat("Nothing to replace", "Nothing to replace");
// Replace: 1 item
assertFormat("{0} apples", "15 apples", 15);
assertFormat("number of apples: {0}", "number of apples: zero", "zero");
assertFormat("you ate {0} apples", "you ate some apples", "some");
// Replace 2 items
assertFormat("{1} text {0}", "second text first", "first", "second");
assertFormat("X {1} text {0}", "X second text first", "first", "second");
assertFormat("{0} text {1} X", "first text second X", "first", "second");
Escaping-тесты:
// Escaping with no replacement
assertFormat("It the world", "Its the world");
assertFormat("It' the world", "It the world");
assertFormat("Open ' and now a second ' (closes)", "Open and now a second (closes)");
assertFormat("It'' the world", "It the world");
assertFormat("'{0}' {1} {2}", "{0} one two", "zero", "one", "two");
// Stays escaped (if end escape is missing)
assertFormat("'{0} {1} {2}", "{0} {1} {2}", "zero", "one", "two");
assertFormat("'{0} {1}' {2}", "{0} {1} two", "zero", "one", "two");
// No matter where we escape, stays escaped
assertFormat("It a {0} world", "Its a {0} world", "blue");
// But we can end escape everywhere
assertFormat("It a {0} world, but not '{1}",
"Its a {0} world, but not always", "blue", "always");
// I think we want this
assertFormat("It' a {0} world, but not {1}",
"It a blue world, but not always", "blue", "always");
// Triple
assertFormat("' '' '", " ' ");
// From oracle docs
assertFormat("'{''}'", "{'}");
// Missing argument (just stays 0)
assertFormat("begin {0} end", "begin {0} end");
// Throws
try {
assertFormat("begin {not_a_number} end", "begin {not_a_number} end");
throw new AssertionError("Should not get here");
} catch (IllegalArgumentException iae) {
// OK
}
В качестве альтернативы вы можете использовать класс NumberFormat:
NumberFormat fmt = NumberFormat.getDecimalFormat();
double value = 12345.6789;
String formatted = fmt.format(value);
// Prints 1,2345.6789 in the default locale
другая очень простая замена для java.text.MessageFormat.format():
public static String format(final String format, final Object... args) {
StringBuilder sb = new StringBuilder();
int cur = 0;
int len = format.length();
while (cur < len) {
int fi = format.indexOf('{', cur);
if (fi != -1) {
sb.append(format.substring(cur, fi));
int si = format.indexOf('}', fi);
if (si != -1) {
String nStr = format.substring(fi + 1, si);
int i = Integer.parseInt(nStr);
sb.append(args[i]);
cur = si + 1;
} else {
sb.append(format.substring(fi));
break;
}
} else {
sb.append(format.substring(cur, len));
break;
}
}
return sb.toString();
}
Возможно, самый простой способ сделать что-то вроде String.format можно, например, с помощью String.replace;
вместо do String.format("Hello %s", "Daniel");
== > "Hello %s".replace("%s", "Daniel")
,
оба дают нам тот же результат, но только второй способ работает на стороне клиента GWT
Как упоминалось выше, существуют форматы GWT для чисел и дат: NumberFormat
и DateTimeFormat
.
Тем не менее, мне понадобилось решение для хорошо известного случая String.format(...)
.
Я в конечном итоге с этим решением, я не знаю, если это неправильно для производительности, но он визуально чист. Я был бы рад услышать какие-либо комментарии по этому поводу или о другом решении.
My String formatter:
public class Strings {
public static String format(final String format, final Object... args) {
String retVal = format;
for (final Object current : args) {
retVal = retVal.replaceFirst("[%][s]", current.toString());
}
return retVal;
}
}
и JUTest, если вы хотите повторно использовать это:
public class StringsTest {
@Test
public final void testFormat() {
this.assertFormat("Some test here %s.", 54);
this.assertFormat("Some test here %s and there %s, and test [%s]. sfsfs !!!", 54, 59, "HAHA");
}
private void assertFormat(final String format, final Object... args) {
Assert.assertEquals("Formatting is not working", String.format(format, args), Strings.format(format, args));
}
}