I try to format a number using NumberFormatter in Android. I use the code bellow and it works perfectly:
NumberFormat formatter = new DecimalFormat("###,###");
String myFormattedString = formatter.format(123456);
But, when i use a pattern with space, like that: new DecimalFormat("###,# ##"); it throws an IllegalArgumentException. I've read documentation about NumberFormatter and DecimalFormatter and found nothing about spaces in patterns. Can anyone explane me why can't i use spaces or how to add them as allowed characters.
Thanks in advance!!!
You can not put spaces in the middle of a number: it is not a valid format.
If you look at the JavaDoc of DecimalFormat, you'll see this:
Prefix:
any Unicode characters except \uFFFE, \uFFFF, and special characters
Suffix:
any Unicode characters except \uFFFE, \uFFFF, and special characters
Number:
Integer Exponentopt
Integer . Fraction Exponentopt
Without copying the entire doc, none of the components of the Number pattern accept spaces, so trying to fit a space in the middle will not work. You can only use spaces in the prefix or suffix.
In a regular JDK this does not throw an exception - it just formats the number as 123,456.
It is not clear what is the space in your example. You have a couple of options for a symbol's role:
decimal separator
group separator
exponent separator
monetary decimal separator
You can set each of these with:
DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols();
symbols.setGroupingSeparator(' ');
formatter.setSymbols(symbols);
I've achieved my goal by making my own formatter using standart formatter and finding prohibited symbols using exeptions. Hope it will be useful for someone else.
public static String getFormattedNumberWithPattern(String aPattern,
float aNumber) {
String lFormattedNumber = null;
String lOriginalPattern = aPattern;
try {
Hashtable<Integer, String> lIlligalChars = new Hashtable<Integer, String>();
// analyze illegal characters
for (int i = aPattern.length() - 1; i >= 0; i--) {
char[] lAux = new char[1];
aPattern.getChars(i, i + 1, lAux, 0);
try {
// if character is illegal, it throws an exception
#SuppressWarnings("unused")
NumberFormat lNumberFormatter = new DecimalFormat("#"
+ lAux[0] + "#");
} catch (IllegalArgumentException e) {
// add illegal chars and indexes to dictionary
lIlligalChars.put(new Integer(i), String.valueOf(lAux[0]));}}
Enumeration<String> lChars = lIlligalChars.elements();
while (lChars.hasMoreElements()) {
String lIllegalChar = lChars.nextElement();
// remove illegal chars from pattern
aPattern = removeChar(aPattern, lIllegalChar.charAt(0));
}
// format number using legal pattern
NumberFormat lNumberFormatter = new DecimalFormat(aPattern);
lFormattedNumber = lNumberFormatter.format(aNumber);
int lLenghtDifference = lOriginalPattern.length()
- lFormattedNumber.length();
// add illegal chars to formatted string using their indexes
Enumeration<Integer> lKeys = lIlligalChars.keys();
while (lKeys.hasMoreElements()) {
Integer lIllegalCharIndex = lKeys.nextElement();
int lInsertIndex = lIllegalCharIndex.intValue()
- lLenghtDifference;
// insert illegal chars into formatted number
if (lInsertIndex >= 0
|| lInsertIndex < lFormattedNumber.length()) {
lFormattedNumber = new StringBuffer(lFormattedNumber)
.insert(lInsertIndex,
lIlligalChars.get(lIllegalCharIndex)
.charAt(0)).toString();
}
}
} catch (Exception e) {
// Log.d("info", "formater error:" + e + "mask: " + aPattern
// + " number:" + aNumber);
}
return lFormattedNumber;
}
public static String removeChar(String s, char c) {
StringBuffer r = new StringBuffer(s.length());
r.setLength(s.length());
int current = 0;
for (int i = 0; i < s.length(); i++) {
char cur = s.charAt(i);
if (cur != c)
r.setCharAt(current++, cur);
}
r.setLength(current);
return r.toString();
}
Related
I want to convert the number "4,471.26" into decimal number "4471.26".
Actually number "4,471.26" is received as String and for further process, need to convert it into Decimal number.
"4,471.26" - is just a format,so value is keeps changing every-time. This value could be anything like "654,654,25.54"
Tried by considering comma (,) as group -separator. But looks like has different ASCII values.
String Tempholder = "4,471.26";
Decimal.valueOf(Tempholder.replaceAll(getGroupSeparator(), ""));
private char getGroupSeparator() {
DecimalFormat decFormat = new DecimalFormat();
DecimalFormatSymbols decSymbols = decFormat.getDecimalFormatSymbols();
return Character.valueOf(decSymbols.getGroupingSeparator());
}
Below code would be temporary solution, but it will not work other geo regions.
String Tempholder = "4,471.26";
Decimal.valueOf(Tempholder.replaceAll(",", ""));
Kindly help ..
you can do it like this :
public class JavaCodes{
public static void main(String[] args) {
String str = new String("4,233.19");
str = str.replaceFirst(",", "");
System.out.println(Double.valueOf(str));
}
}
Ok, some assumptions first
You don't know the locale beforehand
If just one separator is found, and it appears only once, we treat it as a decimal separator (for simplicity)
If just one separator is found but it appears multiple times, it's a thousands separator
Any non-digit character is a valid separator
If there are more than two non-digit characters in a string, that's an error.
So here's the algorithm
Find the first non-digit character and temporarily consider it a decimal separator
If another non-digit character is found
If it's the same as the decimal separator, and we still don't have a thousands seprator, make that the thousands separator and reset the decimal separator
If it's the same as the decimal separator, and we already have a thousands separator, that's an error
If it's not the same as the decimal separator, it's the thousands separator
Remove from the original string all occurrences of the thousands separator (if present)
Substitute the decimal separator with a dot
Parse as double.
And here is the code.
public class Test {
public static double parseNumber(String x) {
Character thousandsSeparator = null;
Character decimalSeparator = null;
for (int i = 0; i < x.length(); i++) {
if (!Character.isDigit(x.charAt(i))) {
if (decimalSeparator == null) {
decimalSeparator = x.charAt(i);
} else {
if (decimalSeparator.equals(x.charAt(i))) {
if (thousandsSeparator == null) {
thousandsSeparator = x.charAt(i);
decimalSeparator = null;
} else {
if (!thousandsSeparator.equals(x.charAt(i))) {
throw new IllegalArgumentException();
}
}
} else {
thousandsSeparator = x.charAt(i);
}
}
}
}
// remove thousands separator
if (thousandsSeparator != null) {
int formerSeparatorPosition;
while ((formerSeparatorPosition = x.indexOf(thousandsSeparator)) != -1) {
x = x.substring(0, formerSeparatorPosition) + x.substring(formerSeparatorPosition + 1);
}
}
// replace decimal separator with a dot
if (decimalSeparator != null) {
x = x.replace(decimalSeparator, '.');
}
return Double.parseDouble(x);
}
public static void main(String args[]) {
System.out.println(parseNumber("123.45"));
System.out.println(parseNumber("123,45"));
System.out.println(parseNumber("1.234,5"));
System.out.println(parseNumber("1,234.5"));
System.out.println(parseNumber("1,234,567.512"));
System.out.println(parseNumber("1.234.567,512"));
ystem.out.println(parseNumber("1.234.567"));
System.out.println(parseNumber("1_234_567|34")); // works with any two characters, if there are just two
try {
System.out.println(parseNumber("1_234_567|34,7")); // invalid
} catch (IllegalArgumentException e) {
System.out.println("Too many separators!");
}
}
}
It is quite circumstantial to parse to a BigDecimal. To a double it is easier
but floating point is just an approximation, and will loose the number's precision. 0.2 == 0.20 == 0.200 == (actually something like) 0.1999999987.
String text = "4,471.26";
DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(Locale.US);
format.setParseBigDecimal(true);
BigDecimal number = (BigDecimal) format.parseObject(text);
// number.scale() == 2
The US locale uses your desired separators.
Thank you guys, for all your suggestion and helps. I figure out the below solution. Convert it into decimal number, And applicable to all geo region.
public static void geoSp() {
String a = "4.233,19";
NumberFormat as = NumberFormat.getInstance();
double myNumber = 0;
try {
myNumber = as.parse(a).doubleValue();
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(String.valueOf(myNumber));
}
gives Out put :- 4233.19
I have a string, which I believed contains some of ISO-8859-1 hex character code
String doc = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n"
And I want to change it into this,
Áo thun bé gái cột dây xanh biển
I have tried this method but no luck
byte[] isoBytes = doc.getBytes("ISO-8859-1");
System.out.println(new String(isoBytes, "UTF-8"));
What is the proper way to convert it? Many thanks for your help!
On the assumption that the #nnnn; sequences are plain old Unicode character representation, I suggest the following approach.
class Cvt {
static String convert(String in) {
String str = in;
int curPos = 0;
while (curPos < str.length()) {
int j = str.indexOf("#x", curPos);
if (j < 0) // no more #x
curPos = str.length();
else {
int k = str.indexOf(';', curPos + 2);
if (k < 0) // unterminated #x
curPos = str.length();
else { // convert #xNNNN;
int n = Integer.parseInt(str.substring(j+2, k), 16);
char[] ch = { (char)n };
str = str.substring(0, j) + new String(ch) + str.substring(k+1);
curPos = j + 1; // after ch
}
}
}
return str;
}
static public void main(String... args) {
String doc = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n";
System.out.println(convert(doc));
}
}
This is very similar to the approach of the previous answer, except for the assumption that the character is a Unicode codepoint and not an 8859-1 codepoint.
And the output is
Áo thun bé gái cột dây xanh biển
There is no hex literal syntax for strings in Java. If you need to support that String format, I would make a helper function which parses that format and builds up a byte array and then parse that as ISO-8859-1.
import java.io.ByteArrayOutputStream;
public class translate {
private static byte[] parseBytesWithHexLiterals(String s) throws Exception {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (!s.isEmpty()) {
if (s.startsWith("#x")) {
s = s.substring(2);
while (s.charAt(0) != ';') {
int i = Integer.parseInt(s.substring(0, 2), 16);
baos.write(i);
s = s.substring(2);
}
} else {
baos.write(s.substring(0, 1).getBytes("US-ASCII")[0]);
}
s = s.substring(1);
}
return baos.toByteArray();
}
public static void main(String[] args) throws Exception {
String doc = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n";
byte[] parsedAsISO88591 = parseBytesWithHexLiterals(doc);
doc = new String(parsedAsISO88591, "ISO-8859-1");
System.out.println(doc); // Print out the string, which is in Unicode internally.
byte[] asUTF8 = doc.getBytes("UTF-8"); // Get a UTF-8 version of the string.
}
}
This is a case where the code can really obscure the requirements. The requirements are a bit uncertain but seem to be to decode a specialized Unicode character entity reference similar to HTML and XML, as documented in the comments.
It is also a somewhat rare case where the advantage of the regular expression engine outweighs any studying needed to understand the pattern language.
String input = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n";
// Hex digits between "#x" and ";" are a Unicode codepoint value
String text = java.util.regex.Pattern.compile("(#x([0-9A-Fa-f]+);)")
.matcher(input)
// group 2 is the matched input between the 2nd ( in the pattern and its paired )
.replaceAll(x -> new String(Character.toChars(Integer.parseInt(x.group(2), 16))));
System.out.println(text);
The matcher function finds candidate strings to replace that match the pattern. The replaceAll function replaces them with the calculated Unicode codepoint. Since a Unicode codepoint might be encoded as two char (UTF-16) values the desired replacement string must be constructed from a char[].
I have to convert string welcome into number format. But when I'm going to execute it throws exception
Exception in thread "main" java.text.ParseException: Unparseable number: "Welcome"
at java.text.NumberFormat.parse(Unknown Source)
at sample.Sam.main(Sam.java:13)
package sample;
import java.text.DecimalFormat;
import java.text.ParseException;
public class Sam {
public static void main(String[] args) throws ParseException {
// TODO Auto-generated method stub
String str="Welcome,#123";
DecimalFormat df = new DecimalFormat(str);
System.out.println( df.parse(str));
}
}
The code you shared does no throw the Exception you copied...
Anyway, here is a code that will extract an integer from any string:
String str="Welcome,#123";
Pattern p = Pattern.compile( "\\D*(\\d+)\\D*" );
Matcher m = p.matcher(str);
if ( m.matches() )
{
int value = Integer.parseInt( m.group(1) );
System.out.println( value );
}
else
{
System.out.println("no number found");
}
You need to declare the format first with one string that is a valid DecimalFormat pattern and then you can parse the real string:
String pattern = "'Welcome,#'0"; //0 is a placeholder, see the link for the others
DecimalFormat df = new DecimalFormat(pattern);
String input = "Welcome,#123";
System.out.println(df.parse(input)); //prints 123
As , is also a placeholder, I have had to quote the text 'Welcome,#'.
Everything is possible, but ..
In that case you have to find just a numbers from that string look look at this
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
For recognize if the char is number should be possible to use that
Character.isDigit(string.charAt(0))
So something like:
String s = "..ag123";
String numStr = "";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(Character.isDigit(s.charAt(i))){
numStr += s.charAt(i);
}
}
Integer.parseInt(numStr);
For demimal you have to also recognize decimal (maybe also thousands?) separator :) But something in this way..
Btw you can still probably use decimal formatter but you need to set pattern for that (not sure if its possible like string pattern) - you cant find it in doc, Im not sure, but it will be something like [^0123456789][123456789][separators][0123456789][^0123456789] (missing nonmandatory and repeating there)
I have a problem converting hexadecimal to a character when the hexadecimal has 3 digits
I have 2 methods which escape and unescape characters over decimal value 127
test\\b8 is produced when test¸ is escaped
The unescape does the following:
for (int i=0, n=node.length; i<n; i++) {
if(c == "\\"){
char c2 = node[i + 1];
char c3 = node[i + 2];
int i= Integer.parseInt(str,16);
char c = (char)i;
System.out.println("Char is:=" + c);
}
}
output - test¸
As you can see I take the first two characters after the slash and convert them into a char. This all works fine. However there are sometimes characters that have 3 hexadecimal digits (for example test\\2d8. This should unescape as test˘). When this enters into my unescape method is won't use all 3 characters. Only the first 2 and therefore the produce wrong results.
Is there a way to determine when to convert 2 or 3 characters
Here's what I would do:
String raw = new String(node); // might be a better way to get a string from the chars
int slashPos = raw.indexOf('\\');
if(slashPos >= 0) {
String hex = raw.substring(slashPos + 1);
int value = Integer.parseInt(hex,16);
}
In this manner, we're not special casing anything for 2, 3, 4, or 100 digits (although I'm sure 100 digits would throw an exception :-) ). Instead, we're using the protocol as a 'milestone' in the string, and then just accepting that everything after the slash is the hex string.
class HexParse {
private static class HexResult {
final boolean exists;
final int value;
HexResult(boolean e, int v) { exists = e; value = v; }
}
private final String raw;
private final HexResult result;
public HexParse(String raw) {
this.raw = raw;
int slashPos = raw.indexOf('\\');
boolean noSlash = slashPos < 0;
boolean noTextAfterSlash = slashPos > raw.length() - 2;
if(noSlash || noTextAfterSlash) {
result = new HexResult(false,0);
} else {
// throws exception if second part of string contains non-hex chars
result = new HexResult(true,Integer.parseInt(raw.substring(slashPos + 1),16));
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(raw).append(" ");
if(result.exists) {
sb.append("has hex of decimal value ").append(result.value);
} else {
sb.append("has no hex");
}
return sb.toString();
}
public static void main(String...args) {
System.out.println(new HexParse("test`hello")); // none
System.out.println(new HexParse("haha\\abcdef")); // hex
System.out.println(new HexParse("good\\f00d")); // hex
System.out.println(new HexParse("\\84b")); // hex
System.out.println(new HexParse("\\")); // none
System.out.println(new HexParse("abcd\\efgh")); //exception
}
}
c:\files\j>javac HexParse.java
c:\files\j>java HexParse
test`hello has no hex
haha\abcdef has hex of decimal value 11259375
good\f00d has hex of decimal value 61453
\84b has hex of decimal value 2123
\ has no hex
Exception in thread "main" java.lang.NumberFormatException: For input string: "e
fgh"
at java.lang.NumberFormatException.forInputString(NumberFormatException.
java:48)
at java.lang.Integer.parseInt(Integer.java:458)
at HexParse.<init>(HexParse.java:21)
at HexParse.main(HexParse.java:
I want to format 3 digit floats in Java so they line up vertically such that they look like:
123.45
99
23.2
45
When I use DecimalFormat class, I get close, but I want to insert spaces when the item has 1 or 2 digits.
My code:
DecimalFormat formatter = new java.text.DecimalFormat("####.##");
float [] floats = [123.45, 99.0, 23.2, 45.0];
for(int i=0; i<floats.length; i++)
{
float value = floats[i];
println(formatter.format(value));
}
It produces:
123.45
99
23.2
45
How can I print it so that all but the first line is shifted over by 1 space?
Try with String.format() (JavaDoc):
public static void main(String args[]){
String format = "%10.2f\n"; // width == 10 and 2 digits after the dot
float [] floats = {123.45f, 99.0f, 23.2f, 45.0f};
for(int i=0; i<floats.length; i++) {
float value = floats[i];
System.out.format(format, value);
}
and the output is :
123.45
99.00
23.20
45.00
This is trivial with a bit of regular expression string replacement.
formatter.format(f).replaceAll("\\G0", " ")
Here it is in context: (see also on ideone.com):
DecimalFormat formatter = new java.text.DecimalFormat("0000.##");
float[] floats = {
123.45f, // 123.45
99.0f, // 99
23.2f, // 23.2
12.345f, // 12.35
.1234f, // .12
010.001f, // 10
};
for(float f : floats) {
String s = formatter.format(f).replaceAll("\\G0", " ");
System.out.println(s);
}
This uses DecimalFormat to do most of the formatting (the zero padding, the optional #, etc) and then uses String.replaceAll(String regex, String replacement) to replace all leading zeroes to spaces.
The regex pattern is \G0. That is, 0 that is preceded by \G, which is the "end of previous match" anchor. The \G is also present at the beginning of the string, and this is what allows leading zeroes (and no other zeroes) to be matched and replaced with spaces.
References
java.util.regex.Pattern
regular-expressions.info - Continuing Matches
On escape sequences
The reason why the pattern \G0 is written as "\\G0" as a Java string literal is because the backslash is an escape character. That is, "\\" is a string of length one, containing the backslash.
References
JLS 3.10.6 Escape Sequences for Character and String Literals
Related questions
How to replace a special character with single slash
Is the char literal '\"' the same as '"' ?(backslash-doublequote vs only-doublequote)
Additional tips
Note that I've used the for-each loop, which results in a much simpler code, thus enhancing readability and minimizing chances of mistakes. I've also kept the floating point variables as float, using the f suffix to declare them as float literals (since they're double by default otherwise), but it needs to be said that generally you should prefer double to float.
See also
Java Language Guide/For-each loop
Java Puzzlers: Prefer double to float.
Just change your first line, replacing '#' characters by '0'. It will solve your problem and produce formatted numbers with the same length, as explicated in the Java API. With that method, your lines will start and end with additional '0' numbers (099.00 for example) :
DecimalFormat formatter = new java.text.DecimalFormat("0000.00");
If you want a correct alignment without theses useless '0', you'll have to create your own formatting method : it doesn't exist in the native Java API.
In the same vein as Benoit's answer, here's a class extending DecimalFormat which ensures a specified minimum length by left-padding formatted numbers with spaces. It's tested (Java 6, 7), more general and provides a working example.
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
public class PaddingDecimalFormat extends DecimalFormat {
private int minimumLength;
/**
* Creates a PaddingDecimalFormat using the given pattern and minimum minimumLength and the symbols for the default locale.
*/
public PaddingDecimalFormat(String pattern, int minLength) {
super(pattern);
minimumLength = minLength;
}
/**
* Creates a PaddingDecimalFormat using the given pattern, symbols and minimum minimumLength.
*/
public PaddingDecimalFormat(String pattern, DecimalFormatSymbols symbols, int minLength) {
super(pattern, symbols);
minimumLength = minLength;
}
#Override
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
int initLength = toAppendTo.length();
super.format(number, toAppendTo, pos);
return pad(toAppendTo, initLength);
}
#Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
int initLength = toAppendTo.length();
super.format(number, toAppendTo, pos);
return pad(toAppendTo, initLength);
}
private StringBuffer pad(StringBuffer toAppendTo, int initLength) {
int numLength = toAppendTo.length() - initLength;
int padLength = minimumLength - numLength;
if (padLength > 0) {
StringBuffer pad = new StringBuffer(padLength);
for(int i = 0; i < padLength; i++) {
pad.append(' ');
}
toAppendTo.insert(initLength, pad);
}
return toAppendTo;
}
public static void main(String[] args) {
PaddingDecimalFormat intFormat = new PaddingDecimalFormat("#", 6);
for (int i = 0; i < 20; i++) {
System.out.println(intFormat.format(i) + intFormat.format(i*i*i));
}
}
}
A method that should answer your problem (I wrote it directly here and did not tested it so you could have some bugs to correct, but at least, you get the idea) :
public class PaddingDecimalFormat extends DecimalFormat {
private int maxIntLength = 1;
public PaddingDecimalFormat(String pattern) {
super(pattern);
}
public void configure(Number[] numbers) {
for(Number number : numbers) {
maxIntLength = Math.max(maxIntLength, Integer.toString(number.intValue()).length());
}
}
#Override
public void format(Number number) {
int padding = maxIntLength - Integer.toString(number.intValue()).length();
StringBuilder sb = new StringBuilder();
for(int i=0; i<padding; i++) {
sb.append(' ');
}
sb.append(super.format(number));
return sb.toString();
}
}
Imagine semicolon and System.out.; formatter like above in question.
printf (" ".substring (("" + Math.round (f)).length ) + formatter.format (f))
I would prefer a log-10-Function, to calculate the size of 999 or 100 to 3, instead of the implicit toString-call, but I just find logarithm naturalis.
Math.round (987.65) gives 987.
("" + 987).length gives 3
"___".substring (3) is the empty string, for a short number (0-9) it will return two blanks.