I am trying to apply this format to a given int number #´###,### I tried with DecimalFormat class but it only allows to have one grouping separator symbol when I need to have two the accute accent for millions and commas for thousands.
So at the end I can format values like 1,000 or millions in this way 1´000,000
I always prefer to use String.format, but I am not sure if there is a Locale that would format numbers like that either. Here is some code that will do the job though.
// Not sure if you wanted to start with a number or a string. Adjust accordingly
String stringValue = "1000000";
float floatValue = Float.valueOf(stringValue);
// Format the string to a known format
String formattedValue = String.format(Locale.US, "%,.2f", floatValue);
// Split the string on the separator
String[] parts = formattedValue.split(",");
// Put the parts back together with the special separators
String specialFormattedString = "";
int partsRemaining = parts.length;
for(int i=0;i<parts.length;i++)
{
specialFormattedString += parts[i];
partsRemaining--;
if(partsRemaining > 1)
specialFormattedString += "`";
else if(partsRemaining == 1)
specialFormattedString += ",";
}
I found useful the link #Roshan provide in comments, this solution is using regex expression and replaceFirst method
public static String audienceFormat(int number) {
String value = String.valueOf(number);
if (value.length() > 6) {
value = value.replaceFirst("(\\d{1,3})(\\d{3})(\\d{3})", "$1\u00B4$2,$3");
} else if (value.length() >=5 && value.length() <= 6) {
value = value.replaceFirst("(\\d{2,3})(\\d{3})", "$1,$2");
} else {
value = value.replaceFirst("(\\d{1})(\\d+)", "$1,$2");
}
return value;
}
I don't know if this solution has a performance impact, also I am rockie with regex, so this code might be shorted.
Try this, these Locale formats in your required format.
List<Locale> locales = Arrays.asList(new Locale("it", "CH"), new Locale("fr", "CH"), new Locale("de", "CH"));
for (Locale locale : locales) {
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
dfs.setCurrencySymbol("");
df.setDecimalFormatSymbols(dfs);
System.out.println(String.format("%5s %15s %15s", locale, format(df.format(1000)), format(df.format(1_000_000))));
}
util method
private static String format(String str) {
int index = str.lastIndexOf('\'');
if (index > 0) {
return new StringBuilder(str).replace(index, index + 1, ",").toString();
}
return str;
}
output
it_CH 1,000.00 1'000,000.00
fr_CH 1,000.00 1'000,000.00
de_CH 1,000.00 1'000,000.00
set df.setMaximumFractionDigits(0); to remove the fractions
output
it_CH 1,000 1'000,000
fr_CH 1,000 1'000,000
de_CH 1,000 1'000,000
Maybe try using this, the "#" in place with the units you want before the space or comma.
String num = "1000500000.574";
String newnew = new DecimalFormat("#,###.##").format(Double.parseDouble(number));
Related
I want to remove elements a supplied Date Format String - for example convert the format "dd/MM/yyyy" to "MM/yyyy" by removing any non-M/y element.
What I'm trying to do is create a localised month/year format based on the existing day/month/year format provided for the Locale.
I've done this using regular expressions, but the solution seems longer than I'd expect.
An example is below:
public static void main(final String[] args) {
System.out.println(filterDateFormat("dd/MM/yyyy HH:mm:ss", 'M', 'y'));
System.out.println(filterDateFormat("MM/yyyy/dd", 'M', 'y'));
System.out.println(filterDateFormat("yyyy-MMM-dd", 'M', 'y'));
}
/**
* Removes {#code charsToRetain} from {#code format}, including any redundant
* separators.
*/
private static String filterDateFormat(final String format, final char...charsToRetain) {
// Match e.g. "ddd-"
final Pattern pattern = Pattern.compile("[" + new String(charsToRetain) + "]+\\p{Punct}?");
final Matcher matcher = pattern.matcher(format);
final StringBuilder builder = new StringBuilder();
while (matcher.find()) {
// Append each match
builder.append(matcher.group());
}
// If the last match is "mmm-", remove the trailing punctuation symbol
return builder.toString().replaceFirst("\\p{Punct}$", "");
}
Let's try a solution for the following date format strings:
String[] formatStrings = { "dd/MM/yyyy HH:mm:ss",
"MM/yyyy/dd",
"yyyy-MMM-dd",
"MM/yy - yy/dd",
"yyabbadabbadooMM" };
The following will analyze strings for a match, then print the first group of the match.
Pattern p = Pattern.compile(REGEX);
for(String formatStr : formatStrings) {
Matcher m = p.matcher(formatStr);
if(m.matches()) {
System.out.println(m.group(1));
}
else {
System.out.println("Didn't match!");
}
}
Now, there are two separate regular expressions I've tried. First:
final String REGEX = "(?:[^My]*)([My]+[^\\w]*[My]+)(?:[^My]*)";
With program output:
MM/yyyy
MM/yyyy
yyyy-MMM
Didn't match!
Didn't match!
Second:
final String REGEX = "(?:[^My]*)((?:[My]+[^\\w]*)+[My]+)(?:[^My]*)";
With program output:
MM/yyyy
MM/yyyy
yyyy-MMM
MM/yy - yy
Didn't match!
Now, let's see what the first regex actually matches to:
(?:[^My]*)([My]+[^\\w]*[My]+)(?:[^My]*) First regex =
(?:[^My]*) Any amount of non-Ms and non-ys (non-capturing)
([My]+ followed by one or more Ms and ys
[^\\w]* optionally separated by non-word characters
(implying they are also not Ms or ys)
[My]+) followed by one or more Ms and ys
(?:[^My]*) finished by any number of non-Ms and non-ys
(non-capturing)
What this means is that at least 2 M/ys are required to match the regex, although you should be careful that something like MM-dd or yy-DD will match as well, because they have two M-or-y regions 1 character long. You can avoid getting into trouble here by just keeping a sanity check on your date format string, such as:
if(formatStr.contains('y') && formatStr.contains('M') && m.matches())
{
String yMString = m.group(1);
... // other logic
}
As for the second regex, here's what it means:
(?:[^My]*)((?:[My]+[^\\w]*)+[My]+)(?:[^My]*) Second regex =
(?:[^My]*) Any amount of non-Ms and non-ys
(non-capturing)
( ) followed by
(?:[My]+ )+[My]+ at least two text segments consisting of
one or more Ms or ys, where each segment is
[^\\w]* optionally separated by non-word characters
(?:[^My]*) finished by any number of non-Ms and non-ys
(non-capturing)
This regex will match a slightly broader series of strings, but it still requires that any separations between Ms and ys be non-words ([^a-zA-Z_0-9]). Additionally, keep in mind that this regex will still match "yy", "MM", or similar strings like "yyy", "yyyy"..., so it would be useful to have a sanity check as described for the previous regular expression.
Additionally, here's a quick example of how one might use the above to manipulate a single date format string:
LocalDateTime date = LocalDateTime.now();
String dateFormatString = "dd/MM/yyyy H:m:s";
System.out.println("Old Format: \"" + dateFormatString + "\" = " +
date.format(DateTimeFormatter.ofPattern(dateFormatString)));
Pattern p = Pattern.compile("(?:[^My]*)([My]+[^\\w]*[My]+)(?:[^My]*)");
Matcher m = p.matcher(dateFormatString);
if(dateFormatString.contains("y") && dateFormatString.contains("M") && m.matches())
{
dateFormatString = m.group(1);
System.out.println("New Format: \"" + dateFormatString + "\" = " +
date.format(DateTimeFormatter.ofPattern(dateFormatString)));
}
else
{
throw new IllegalArgumentException("Couldn't shorten date format string!");
}
Output:
Old Format: "dd/MM/yyyy H:m:s" = 14/08/2019 16:55:45
New Format: "MM/yyyy" = 08/2019
I'll try to answer with the understanding of my question : how do I remove from a list/table/array of String, elements that does not exactly follow the patern 'dd/MM'.
so I'm looking for a function that looks like
public List<String> removeUnWantedDateFormat(List<String> input)
We can expect, from my knowledge on Dateformat, only 4 possibilities that you would want, hoping i dont miss any, which are "MM/yyyy", "MMM/yyyy", "MM/yy", "MM/yyyy". So that we know what we are looking for we can do an easy function.
public List<String> removeUnWantedDateFormat(List<String> input) {
String s1 = "MM/yyyy";
string s2 = "MMM/yyyy";
String s3 = "MM/yy";
string s4 = "MMM/yy";
for (String format:input) {
if (!s1.equals(format) && s2.equals(format) && s3.equals(format) && s4.equals(format))
input.remove(format);
}
return input;
}
Better not to use regex if you can, it costs a lot of resources. And great improvement would be to use an enum of the date format you accept, like this you have better control over it, and even replace them.
Hope this will help, cheers
edit: after i saw the comment, i think it would be better to use contains instead of equals, should work like a charm and instead of remove,
input = string expected.
so it would looks more like:
public List<String> removeUnWantedDateFormat(List<String> input) {
List<String> comparaisons = new ArrayList<>();
comparaison.add("MMM/yyyy");
comparaison.add("MMM/yy");
comparaison.add("MM/yyyy");
comparaison.add("MM/yy");
for (String format:input) {
for(String comparaison: comparaisons)
if (format.contains(comparaison)) {
format = comparaison;
break;
}
}
return input;
}
I know it has been asked a lot of times, and I did manage to get a lot of Google & stackoverflow results on this matter, but the one I tried doesn't work and gives an IllegalArgumentExceptions.
How do I convert a double price to a String, with a Euro[€]/Dollar[$] currency given?
This is my code:
// Convert Price-Double to String
public static String doubleToPrice(double price, String currency){
// Create DecimalFormat based on Currency
// Default (Euro)
// Use "€##.###,##"
DecimalFormat formatter = new DecimalFormat("€##.###,##");
// currency parameter given
if(currency != null && !currency.trim().equals(""))
// Dollar
// Use "$##,###.##"
if(currency.trim().equals("$"))
formatter = new DecimalFormat("$##,###.##");
// no currency parameter given: use Config.CURRENCY instead
else
// Dollar
// Use "$##,###.##"
if(compare(currency, Config.CURRENCY))
formatter = new DecimalFormat("$##,###.##");
// Format the string
String priceString = formatter.format(price);
// Add a space between the currency and the price value
priceString = priceString.substring(0, 1) + " " + priceString.substring(1, priceString.length());
// Replace last two "00" digits for "-"
if(priceString.endsWith("00")){
int i = priceString.lastIndexOf("00");
priceString = new StringBuilder(priceString).replace(i, i+2, "-").toString();
}
return priceString;
}
When I use the € as currency parameter, I'm getting an IllegalArgumentException at DecimalFormatter formatter = new DecimalFormatter("€##.###,##");. Does anyone know what is wrong with my DecimalFormatter prefix?
I'm also getting an IllegalArgumentException when I remove the € or $ from my Formatter prefix.
Thanks in advance for the responses.
EDIT 1 (Some examples):
doubleToPrice(4.23, "€"); should return "€ 4,23"
doubleToPrice(7.3, "€"); should return "€ 7,30"
doubleToPrice(9, "€"); should return "€ 9,-"
doubleToPrice(12345678.9, "€"); should return "€ 12.345.678,90"
doubleToPrice(0.39, "€"); should return "€ 0,39"
doubleToPrice(0.06, "€"); should return "€ 0,06"
doubleToPrice(4.23, "$"); should return "$ 4.23"
doubleToPrice(7.3, "$"); should return "$ 7.30"
doubleToPrice(9, "$"); should return "$ 9.-"
doubleToPrice(12345678.9, "$"); should return "$ 12,345,678.90"
doubleToPrice(0.39, "$"); should return "$ 0.39"
doubleToPrice(0.06, "$"); should return "$ 0.06"
EDIT 2 / Solution:
Accepted Hirak's answer, since he covered most of the things I wanted after his edit. Still, here is the finished code with the "hacks" for converting 00 to -.
// Convert Price-Double to String
public static String doubleToPrice(double price, char currency){
// Define currency to be used
char curr = Config.CURRENCY;
if(currency == '€' || currency == '$')
curr = currency;
// Format the string using a DecimalFormat
Locale locale = new Locale("de", "DE");
if(curr == '$')
locale = new Locale("en", "US");
DecimalFormatSymbols sym = new DecimalFormatSymbols(locale);
sym.setGroupingSeparator('.');
if(curr == '$')
sym.setGroupingSeparator(',');
DecimalFormat formatter = (DecimalFormat)NumberFormat.getNumberInstance(locale);
formatter.applyPattern(curr + "##,##0.00");
formatter.setDecimalFormatSymbols(sym);
String returnString = formatter.format(price);
// Replace "00" after the comma with "-"
if(returnString.endsWith("00")){
int i = returnString.lastIndexOf("00");
returnString = new StringBuilder(returnString).replace(i, i+2, "-").toString();
}
// Add space between currency-symbol and price
returnString = returnString.substring(0, 1) + " " + returnString.substring(1, returnString.length());
Log.i("PRICE FORMATTING", "double that goes in [" + price + "]; string that comes out [" + returnString + "]");
return returnString;
}
Is this what you want? (note that, even though I have put DOT as decimal separator, in the output, you will get COMMA as decimal deparator, because Java decides at runtime based on the locale.)
static String doubleToPrice(double dbl,char currency) {
Locale locale =null;
if(currency=='€') {
locale = new Locale("fr", "FR");
}else {
locale = new Locale("en", "EN");
}//Add locales as per need.
DecimalFormatSymbols sym = new DecimalFormatSymbols(locale);
sym.setGroupingSeparator('.');
DecimalFormat decimalFormat = (DecimalFormat)
NumberFormat.getNumberInstance(locale);
decimalFormat.applyPattern(currency+"##,###.00");
decimalFormat.setDecimalFormatSymbols(sym);
return decimalFormat.format(dbl);
}
I have below strings:
String str1 = "$123.00";
String str2 = "$(123.05)";
String str3 = "incorrectString";
I want to check and output like:
if(str1 is a valid currency){
System.out.println("Str1 Valid");
}else{
System.out.println("Str1 InValid");
}
if(str2 is a valid currency){
System.out.println("Str2 Valid");
}else{
System.out.println("Str2 InValid");
}
if(str3 is a valid currency){
System.out.println("Str3 Valid");
}else{
System.out.println("Str3 InValid");
}
UseCase: I am parsing a pdf using pdfbox. Given a searchterm say "abc", I want to read next token after the search term. For this purpose, I am searching for the search term in parsed pdf text and then reading the next token to that search term.
The token should be a valid currency. But there could be a case where in "abc" is present at two different places in a page with one having valid currency token next to it while the other not.
So I want to put in a check that if the token I am reading is not a valid currency token, break the loop and continue the search on the page.
I did it as below:
if (tokenRead.length() > 0) {
String temp = tokenRead.replace("$", "").replaceAll("\\(", "");
char checkFirstChar = temp.trim().charAt(0);
if (!(checkFirstChar >= '0' && checkFirstChar <= '9')) {
System.out.println("breaking");
break;
}
}
This works, but I believe there should be a elegant solution using NumberFormat.
Hence the question!
Thanks for reading!
NumberFormat has nothing out of the box for your use case.
A possible solution I could come up with is this:
Currency currency = Currency.getInstance(currentLocale);
String symbol = currency.getSymbol();
if(string.startsWith(symbol) || string.endsWith(symbol)){
System.out.println("valid");
}else{
System.out.println("invalid");
}
But then you still need to check if the rest of the string can be parsed to a number.
Therefore I recommend to have a look at Apache Commons Currency Validator, it may fit your needs:
#Test
public void test() {
BigDecimalValidator validator = CurrencyValidator.getInstance();
BigDecimal amount = validator.validate("$123.00", Locale.US);
assertNotNull(amount);
//remove the brackets since this is something unusual
String in = "$(123.00)".replaceAll("\\(", "").replace(')', ' ').trim();
amount = validator.validate(in, Locale.US);
assertNotNull(amount);
amount = validator.validate("invalid", Locale.US);
assertNull(amount);
}
You could try DecimalFormat. It allows you to handle positive and negative value patterns separately using ;:
List<String> list = new ArrayList<>();
list.add("$123.00");
list.add("$(123.05)");
list.add("incorrectString");
NumberFormat nf = new DecimalFormat("¤#.00;¤(#.00)", new DecimalFormatSymbols(Locale.US));
try {
for(String str : list){
nf.parse(str);
}
} catch (ParseException e) {
System.out.println(e.getMessage());
}
I have an android application and today I have got a crash report which contains this:
This exception trigger when the application tries to parse string number which is provided by the user.
It is obvious that problem is the application cannot parse Hindi numbers! So, how can I solve this?
Regex
Using regex would be better if you want to match any unicode digits.The regex would be \\p{N}+ and here's how to use it:
Matcher m=Pattern.compile("\\p{N}+").matcher(input);
if(m.find())
{
System.out.println(m.group());
}
Locale
To answer your question you should use NumberFormat as mentioned in docs. Specify a Locale for NumberFormat.
NumberFormat nf = NumberFormat.getInstance(new Locale("hi", "IN"));
nf.parse(input);
You can use Character.getNumericValue(char).
The good thing about this method is that it can do what you need.
But to work in valid you should implement in your application support for local.
NumberFormat format = NumberFormat.getInstance(new Locale("hin","IND"));
Number parse = format.parse("१");
System.out.println(parse);
Prints 1.
Try this. This will remove non numeric characters.
Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher(str); // str is input String
while(m.find()) {
System.out.println(m.group(1));
}
If you are dealing with double(with decimal places). you can try this
String text = "123.0114cc";
String numOnly = text.replaceAll("\\p{Alpha}","");
double numVal = Double.valueOf(numOnly);
System.out.println(numVal);
Use
BigDecimal bigDecimal = new BigDecimal(YOUR_VALUE);
before applying the regex, as the BigDecimal supports 12 integers, 12.35 decimal, and 12 $ currency, 12% percentage and its localized value.
You can use the following method which receives a string and converts every Indian digit inside it to Arabic.
public static String convertAllIndianToArabic(String str)
{
for(int i=0; i<str.length(); i++)
{
if(str.charAt(i)=='٠')
str = str.substring(0, i)+"0"+str.substring(i+1);
else if(str.charAt(i)=='١')
str = str.substring(0, i)+"1"+str.substring(i+1);
else if(str.charAt(i)=='٢')
str = str.substring(0, i)+"2"+str.substring(i+1);
else if(str.charAt(i)=='٣')
str = str.substring(0, i)+"3"+str.substring(i+1);
else if(str.charAt(i)=='٤')
str = str.substring(0, i)+"4"+str.substring(i+1);
else if(str.charAt(i)=='٥')
str = str.substring(0, i)+"5"+str.substring(i+1);
else if(str.charAt(i)=='٦')
str = str.substring(0, i)+"6"+str.substring(i+1);
else if(str.charAt(i)=='٧')
str = str.substring(0, i)+"7"+str.substring(i+1);
else if(str.charAt(i)=='٨')
str = str.substring(0, i)+"8"+str.substring(i+1);
else if(str.charAt(i)=='٩')
str = str.substring(0, i)+"9"+str.substring(i+1);
}
return str;
}
I have a list of strings and I'd like to convert them to float if a pattern is matched.
Here are some values and the expected result:
1000 -> 1000.0
1.000 -> 1000.0
1.000,000 -> 1000.0
-1.000,000 -> -1000.0
9,132 -> 9.132
1,000.00 -> invalid
30.10.2010 -> invalid
1,000.000,00 -> invalid
I tried this code for checking if a number is valid, but the pattern is never matched:
Pattern pattern = Pattern.compile("#.###,###");
for(String s : list){
Matcher m = pattern.matcher(s);
if(m.matches()){
//convert
}
}
Beside that I've tried to use this code:
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance();
for(String s : list){
try {
Number num = df.parse(s);
//..
} catch (ParseException e) {
}
}
The problem with this code is, that no pattern-based validation is performed. E.g. a date like 2012/05/30 is converted to 2012.
So how can I either define a valid pattern or configure DecimalFormat for my needs?
The Pattern class works with regular expressions. You probably want this:
Pattern pattern = Pattern.compile("-?\d\.\d{1,3}(,\d{1,3})?");
You probably want to tune this regex depending on exactly what formats you want or don't want to match.
I think this is what you want. The comments should explain it.
#Test
public void testAllValues() {
testValue("1000", "1000");
testValue("1.000,000", "1000");
testValue("-1.000,000", "-1000");
testValue("9,132", "9.132");
testValue("1,000.00", null);
testValue("30.10.2010", null);
testValue("1,000.000,00", null);
}
private void testValue(String germanString, String usString) {
BigDecimal germanDecimal = (BigDecimal) parse(germanString);
if (usString != null) {
BigDecimal usDecimal = new BigDecimal(usString);
assertEquals("German " + germanString + " did not equal US " + usString, 0, germanDecimal.compareTo(usDecimal));
} else {
assertEquals("German " + germanString + " should not have been pareseable", null, germanDecimal);
}
}
public BigDecimal parse(String s) {
// Patch because parse doesn't enforce the number of digits between the
// grouping character (dot).
if (!Pattern.matches("[^.]*(\\.\\d{3})*[^.]*", s)) {
return null;
}
DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(Locale.GERMANY);
df.setParseBigDecimal(true);
// Have to use the ParsePosition API or else it will silently stop
// parsing even though some of the characters weren't part of the parsed
// number.
ParsePosition position = new ParsePosition(0);
BigDecimal parsed = (BigDecimal) df.parse(s, position);
// getErrorIndex() doesn't seem to accurately reflect errors, but
// getIndex() does reflect how far we successfully parsed.
if (position.getIndex() == s.length()) {
return parsed;
} else {
return null;
}
}
Try
System.out.println("1,000.000,00".matches("^[+-]?\\d+(\\.\\d{3})*(,\\d+)?"));
I am not sure if your number can start with + so added it just in case. Also don't know if 0100000.000.000,1234 should be valid. If not tell why and regex will be corrected.
If the pattern is the comma try:
String[] splitted = string.split(",")
If size of splitted > 2 --> invalid.
If splitted.size == 2 && splitted[1].split(".") > 0 --> invalid also.
If the format is fine --> remove all points, replace comma with point, parse string after comma into int and connect the pieces.
A very simple approach but it works...