Primitive question, but how do I format strings like this:
"Step {1} of {2}"
by substituting variables using Java? In C# it's easy.
Take a look at String.format. Note, however, that it takes format specifiers similar to those of C's printf family of functions -- for example:
String.format("Hello %s, %d", "world", 42);
Would return "Hello world, 42". You may find this helpful when learning about the format specifiers. Andy Thomas-Cramer was kind enough to leave this link in a comment below, which appears to point to the official spec. The most commonly used ones are:
%s - insert a string
%d - insert a signed integer (decimal)
%f - insert a real number, standard notation
This is radically different from C#, which uses positional references with an optional format specifier. That means that you can't do things like:
String.format("The {0} is repeated again: {0}", "word");
... without actually repeating the parameter passed to printf/format. (see The Scrum Meister's comment below)
If you just want to print the result directly, you may find System.out.printf (PrintStream.printf) to your liking.
In addition to String.format, also take a look java.text.MessageFormat. The format less terse and a bit closer to the C# example you've provided and you can use it for parsing as well.
For example:
int someNumber = 42;
String someString = "foobar";
Object[] args = {new Long(someNumber), someString};
MessageFormat fmt = new MessageFormat("String is \"{1}\", number is {0}.");
System.out.println(fmt.format(args));
A nicer example takes advantage of the varargs and autoboxing improvements in Java 1.5 and turns the above into a one-liner:
MessageFormat.format("String is \"{1}\", number is {0}.", 42, "foobar");
MessageFormat is a little bit nicer for doing i18nized plurals with the choice modifier. To specify a message that correctly uses the singular form when a variable is 1 and plural otherwise, you can do something like this:
String formatString = "there were {0} {0,choice,0#objects|1#object|1<objects}";
MessageFormat fmt = new MessageFormat(formatString);
fmt.format(new Object[] { new Long(numberOfObjects) });
String#format
The most frequent way to format a String is using this static method, that is long available since Java 5 and has two overloaded methods:
String#format(String format, Object args...)
String#format(Locale l, String format, Object args...)
The method is easy to use and the format pattern is defined by underlying formatter.
String step1 = "one";
String step2 = "two";
// results in "Step one of two"
String string = String.format("Step %s of %s", step1, step2);
You can pass a Locale to respect the language and regional specification. Refer to this answer for more information: https://stackoverflow.com/a/6431949/3764965 (credits to Martin Törnwall).
MessageFormat
The MessageFormat class is available since the first version of Java and is suitable for internationalization. In the simplest form, there is a static method for formatting:
MessageFormat#format​(String pattern, Object... arguments)
String step1 = "one";
String step2 = "two";
// results in "Step one of two"
String string = MessageFormat.format("Step {0} of {1}", step1, step2);
Remember MessageFormat follows a specific pattern different from String#format, refer to its JavaDoc for more details: MessageFormat - patterns.
It is possible to use Locale, however, one has to instantiate the object of the class and pass it to the constructor since the static method above uses the default constructor with the default locale. Refer to this answer for more information: https://stackoverflow.com/a/6432100/3764965 (credits to ataylor).
Non standard JDK solutions
There are plenty of ways to format Strings using external libraries. They add little to no benefit if the libraries are imported solely for the purpose of String formatting. Few examples:
Apache Commons: StringSubstitutor, examples in its JavaDoc.
Cactoos: FormattedText, examples here.
Interestingly, Guava doesn't plan to add formatting or templating features: #1142.
... and other custom implementations.
Feel free to add more, however, I find no reason to further expand this section.
Alternative since Java 15
There is a new instance method called String#formatted(Object... args) as of Java 15.
The internal implementation is the same as String#format(String format, Object... args).
Formats using this string as the format string, and the supplied arguments.
String step1 = "one";
String step2 = "two";
// results in "Step one of two"
String string = "Step %s of %s".formatted(step1, step2);
Advantage: The difference is that the method is not static and the formatting pattern is a string itself from which a new one is created based on the args. This allows chaining to build the format itself first.
Disadvantage: There is no overloaded method with Locale, therefore uses the default one. If you need to use a custom Locale, you have to stick with String#format(Locale l, String format, Object... args).
If you choose not to use String.format, the other option is the + binary operator
String str = "Step " + a + " of " + b;
This is the equivalent of
new StringBuilder("Step ").append(String.valueOf(1)).append(" of ").append(String.valueOf(2));
Whichever you use is your choice. StringBuilder is faster, but the speed difference is marginal. I prefer to use the + operator (which does a StringBuilder.append(String.valueOf(X))) and find it easier to read.
I've wrote my simple method for it :
public class SomeCommons {
/** Message Format like 'Some String {0} / {1}' with arguments */
public static String msgFormat(String s, Object... args) {
return new MessageFormat(s).format(args);
}
}
so you can use it as:
SomeCommons.msfgFormat("Step {1} of {2}", 1 , "two");
public class StringFormat {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("================================");
for(int i=0;i<3;i++){
String s1=sc.next();
int x=sc.nextInt();
System.out.println(String.format("%-15s%03d",s1,x));
}
System.out.println("================================");
}
}
outpot
"================================"
ved15space123
ved15space123
ved15space123
"================================
Java solution
The "-" is used to left indent
The "15" makes the String's minimum length it takes up be 15
The "s" (which is a few characters after %) will be substituted by our String
The 0 pads our integer with 0s on the left
The 3 makes our integer be a minimum length of 3
This solution worked for me. I needed to create urls for a REST client dynamically so I created this method, so you just have to pass the restURL like this
/customer/{0}/user/{1}/order
and add as many params as you need:
public String createURL (String restURL, Object ... params) {
return new MessageFormat(restURL).format(params);
}
You just have to call this method like this:
createURL("/customer/{0}/user/{1}/order", 123, 321);
The output
"/customer/123/user/321/order"
I wrote this function it does just the right thing. Interpolate a word starting with $ with the value of the variable of the same name.
private static String interpol1(String x){
Field[] ffield = Main.class.getDeclaredFields();
String[] test = x.split(" ") ;
for (String v : test ) {
for ( Field n: ffield ) {
if(v.startsWith("$") && ( n.getName().equals(v.substring(1)) )){
try {
x = x.replace("$" + v.substring(1), String.valueOf( n.get(null)));
}catch (Exception e){
System.out.println("");
}
}
}
}
return x;
}
Apache Commons StringSubstitutor provides a simple and readable way to do format Strings with named variables.
import org.apache.commons.text.StringSubstitutor;
// ...
Map<String, String> values = new HashMap<>();
values.put("animal", "quick brown fox");
values.put("target", "lazy dog");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("The ${animal} jumped over the ${target}.");
// "The quick brown fox jumped over the lazy dog."
This class supports providing default values for variables.
String result = sub.replace("The number is ${undefined.property:-42}.");
// "The number is 42."
To use recursive variable replacement, call setEnableSubstitutionInVariables(true);.
Map<String, String> values = new HashMap<>();
values.put("b", "c");
values.put("ac", "Test");
StringSubstitutor sub = new StringSubstitutor(values);
sub.setEnableSubstitutionInVariables(true);
String result = sub.replace("${a${b}}");
// "Test"
The org.apache.commons.text.StringSubstitutor helper class from Apache Commons Text provides named variable substitution
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String resolved = new StringSubstitutor(valuesMap).replace("The ${animal} jumped over the ${target}.");
System.out.println(resolved); // The quick brown fox jumped over the lazy dog.
Related
I would like to write a function that would enable me to do the following
inputs: variable number of objects of any type
output: a string that would be NameObj1=ValueObj1, ..., NameObjN=ValueObjN
All objects I would pass to the function would have a toString() method.
Example:
double x=1.1; int y=2; ClassA a
theFunction(x,y,a)
=> this would output "x=1.1, y=1, a=[whatever a.toString() output]"
Is that possible ?
here's something close:
you can write a var-arg function like so:
public static String describeArguments (Object... arguments) {
StringBuilder output = new StringBuilder();
int counter = 1;
for (Object argument : arguments) {
output.append("object #").append(counter++).append(": ").append(argument.toString());
}
return output.toString();
}
strictly speaking method arguments dont have names. you could retrieve the argument parameter name using reflection if the symbol tables werent stripped out #compile time, but its brutish and ugly.
There's no way of getting what you wrote to be the "name" of a variable, because the only way of referencing it, is by itself, and by value is not possible as well.
As mentioned in other answers and comments there is no "name of an object". But if the objects you are interested in are all fields of one class, you could write a function that uses reflection to access that objects fields and prints their names.
Take a look at the reflection tutorial. There is also an example that is very close to what you might have in mind.
You can create a map like
Map<String,Object> map= new HashMap<String,Object>();
map.put("x", 1.1);
map.put("y",2);
map.put("a", MyClass.class);
And call theFunction(map), where theFunction is:
public void theFunction(HashMap<String,Object> list) {
StringBuilder sb = new StringBuilder();
for(String key:list.keySet()) {
try {
Object currentObject = list.get(key);
sb.append(key+"="+currentObject.getClass().getMethod("toString").invoke(currentObject)+" ");
}
catch(Exception e){}
}
System.out.println(sb.toString());
}
Is it possible to create in java something like this someFunction("%s, %s, %s", 1, true, "qwe"); where result should be 1 true qwe?
I tried it with different approaches such as using PrintStream and some other classes but I can't figure out how to do it.
So far one things that seem certain is the definition:
public static String prepare(String format, Object... arguments) {
return ???
}
But I cannot figure out how to do it past that. Can you give me some advices?
You can use String.format method:
public static String prepare(String format, Object... arguments) {
// do same sanity checks if needed
return String.format(format, arguments);
}
This is what String.format does, but I assume that you know that already, and would like to build your own function.
The header of the function that you have is correct. Now you need to make a counter count initially set to zero, create a StringBuilder, and run a loop that scans the format string.
When your loop encounters a character other than the '%', append that character to the StringBuilder. Otherwise, check the next character for a format that your program recognizes, and grab the object at the position count from the arguments array. Format the object as required, and append the result to StringBuilder; increment count.
Once the loop is over, StringBuilder contains the result string that you return to the callers.
Of course this is only a skeleton of the algorithm. A real implementation needs to take care of many other important things, such as
Checking that the count in the loop does not advance past the end of the arguments array
Checking that the final count is not less than the number of objects in the arguments
Checking that the format specifier can be applied to the object from the arguments array
and so on.
Yes, this is exactly what String.format() does:
public class Test {
public static void main(String[] args) {
System.out.println(format("%s %s %s", 12, "A", true));
}
public static String format(String format, Object ... args) {
return String.format(format, args);
}
}
That's what String.format() is meant for
String.format("%s, %s, %s", 1, true, "666");
In your case,
return String.format(format, arguments);
I find myself very frequently wanting to write reusable strings with parameter placeholders in them, almost exactly like what you'd find in an SQL PreparedStatement.
Here's an example:
private static final String warning = "You requested ? but were assigned ? instead.";
public void addWarning(Element E, String requested, String actual){
warning.addParam(0, requested);
warning.addParam(1, actual);
e.setText(warning);
//warning.reset() or something, I haven't sorted that out yet.
}
Does something like this exist already in Java? Or, is there a better way to address something like this?
What I'm really asking: is this ideal?
String.format()
Since Java 5, you can use String.format to parametrize Strings. Example:
String fs;
fs = String.format("The value of the float " +
"variable is %f, while " +
"the value of the " +
"integer variable is %d, " +
" and the string is %s",
floatVar, intVar, stringVar);
See http://docs.oracle.com/javase/tutorial/java/data/strings.html
Alternatively, you could just create a wrapper for the String to do something more fancy.
MessageFormat
Per the comment by Max and answer by Affe, you can localize your parameterized String with the MessageFormat class.
You could use String.format. Something like:
String message = String.format("You requested %2$s but were assigned %1$s", "foo", "bar");
will generate
"You requested bar but were assigned foo"
It is built-in, yes. The class you're looking for is java.text.MessageFormat
Java String formatter
The String class provides the following format method, http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/String.html. For example (as per the original post):
private final static String string = "You requested %s but were assigned %s instead.";
public void addWarning(Element e, String requested, String actual) {
String warning = String.format(string, requested, actual);
e.setText(warning);
I would probably do something like:
private final String warning = String.format("You requested %s but were assigned %s instead.", requested, actual);
If you wanted to put the parameterized string before the call to format the string you could do something like what you see below, although this is less clear.
Neither of these solutions are inherently localizeable; you may want to consider something like a .properties file if you wish to support non-English locales.
private static final String warning = "You requested %s but were assigned %s instead.";
public void addWarning(Element E, String requested, String actual){
e.setText(String.format(warning, requested, actual);
//warning.reset() or something, I haven't sorted that out yet.
}
the formatter can do this for you (with some extras as adding leading zeroes spacing things out and more)
private static final String warning = "You requested %1$s but were assigned %2$s instead.";
public void addWarning(Element E, String requested, String actual){
Formatter f = new Formatter();//you'll need to recreate it each time
try{
f.format(warning,requested,actual);
e.setText(f.out().toString());
}finally{f.close();}
}
Since Java 15, String has a method formatted equivalent to String.format.
So you can directly use "var1 is %s, var2 is %s".formatted(var1, var2);
Java documentation
Well if your String was final you sure wouldn't be able to modify it later. I don't know if maybe you could find a better use case for that kind of thing as you could simply do:
public void addWarning(Element E, String requested, String actual){
String warning = "You requested" + requested + " but were assigned " + actual + " instead."
e.setText(warning);
}
I'm having trouble returning arrays from a custom method. It compiles fine but I get back:
[Ljava.lang.String;#20cf2c80
Press any key to continue . . .
I use:
System.out.println(getItem(1));
code:
public static String[] getItem(int e) {
String[] stats = new String[7];
String name = "Null";
String desc = "None";
String typeOf = "0";
String attackAdd = "0";
String defenseAdd = "0";
String canSell = "true";
String canEat = "false";
String earnedCoins = "0";
if (e == 1) {
name = "Pickaxe";
desc = "Can be used to mine with.";
typeOf = "2";
}
return new String[] { name, desc, typeOf};
}
Help? :\
The toString() method of an array object actually doesn't go through and produce a string representation of the contents of the array, which is what I think you wanted to do. For that you'll need Arrays.toString().
System.out.println(Arrays.toString(getItem(1)));
The notation [Ljava.lang.String is Java code for a String array - in general, the default string representation of an array is [L followed by the type of the array's elements. Then you get a semicolon and the memory address (or some sort of locally unique ID) of the array.
That's not an error. The JVM simply prints the address of the array since it doesn't print its content. Try this and see what happens now?
System.out.println(getItem(1)[0]);
On Object.toString()
The reason why you're getting such string is because arrays simply inherit and not #Override the Object.toString() method.
The toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character #, and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of:
getClass().getName() + '#' + Integer.toHexString(hashCode())
To return a String representation of an array that lists its elements, you can use e.g. Arrays.toString, and for "multidimensional" arrays Arrays.deepToString
Related questions
toString() in Java
Simplest way to print an array in Java
On deepEquals and deepToString for "multidimensional" arrays:
Java Arrays.equals() returns false for two dimensional arrays.
On defining your own type
It needs to be said that your usage of String[] is not the best design choice.
Things would be so much better had you defined your own class BasicItem supported by various enum, with as many final fields as is practical to enforce immutability; perhaps something like this:
public enum ItemType {
KNIFE, SWORD, AXE;
}
public enum Attribute {
SELLABLE, EDIBLE;
}
public class BasicItem {
final String name;
final String desc;
final ItemType type;
final int attackAdd;
final int defenseAdd;
final Set<Attribute> attributes;
//...
}
You should really take advantage all the benefits of good object-oriented design.
See also
Effective Java 2nd Edition
Item 50: Avoid strings where other types are more appropriate
Item 25: Prefer lists to arrays
Item 30: Use enums instead of int constants
Item 32: Use EnumSet instead of bit fields
Item 15: Minimize mutability
Java Tutorials/Enums
Java Tutorials/Object Oriented Programming Concepts
Could someone show me a practical a use of the ... array method delcaration?
Is it just a way to declare an optional parameter instead of passing null value?
public void add(int a, int... b) {
// do something
}
add(1);
add(1,2,3);
String.format is a pretty good practical example. The method doesn't know how many formatters will appear in the format string, so it accepts a variable number of arguments, and there should be a one-to-one mapping of formatters (in the format string) to objects passed into the method.
It's not so much a way of declaring an option parameter rather than null, as it is to declare a method that can deal with multiple arguments. Basing my example off of mine, you could write a sum method that takes a variable number of arguments and sums them all together:
public int sum(int... ns)
{
int sum = 0;
for (int n : ns) {
sum += n;
}
return sum;
}
That way, you could pass in 2, 3, 4, or even 100 numbers to sum, depending on your need at the time.
It's a shorthand for you when you're writing code that will use a function that can take an array as a parameter.
It's generally easier to write add(1,2,3,4); than it is to write add(new int[] {1,2,3,4});, right? It's also clearer when it needs to be read and maintained by future programmers later.
Think about it this way: which function would you call, the one where you have to create an array every time, or the one where you can just pass in as many parameters as you want?
The Formatter class is such a practical use:
Formatter formatter = new Formatter(sb, Locale.US);
formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d")
An arbitrary number of parameters can be passed in to be formatted using the first parameter, which is the format string itself.
Before varargs a method that took an arbitrary number of values required you to create an array put the values into the array prior to invoking the method. Example here is how the MessageFormat class to format a message used to look:
Object[] arguments = {
new Integer(7),
new Date(),
"a disturbance in the Force"
};
String result = MessageFormat.format(
"At {1,time} on {1,date}, there was {2} on planet "
+ "{0,number,integer}.", arguments);
It is still true that multiple arguments must be passed in an array, but the varargs feature automates and hides the process. Furthermore, it is upward compatible with preexisting APIs. So, for example, the MessageFormat.format method now has this declaration:
public static String format(String pattern,
Object... arguments);
The three periods after the final parameter's type indicate that the final argument may be passed as an array or as a sequence of arguments. Varargs can be used only in the final argument position. Given the new varargs declaration for MessageFormat.format, the above invocation may be replaced by the following shorter and sweeter invocation:
String result = MessageFormat.format(
"At {1,time} on {1,date}, there was {2} on planet "
+ "{0,number,integer}.",
7, new Date(), "a disturbance in the Force");
So when should you use varargs? As a client, you should take advantage of them whenever the API offers them. Important uses in core APIs include reflection, message formatting, and the new printf facility. As an API designer, you should use them sparingly, only when the benefit is truly compelling. Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.
From: http://java.sun.com/j2se/1.5.0/docs/guide/language/varargs.html
Derived from you example you could make a method to have the sum of all the ints you pass to the method:
public int sum(int a, int... b) {
int sum = a;
for (int i : b) {
sum += i;
}
return sum;
}