String formatting in Dart - dart

I was looking up string classes and some other resources, trying to see how to format strings. Primarily, I am trying to Pad out a number to a string, but not precision.
example:
int a = 0, b = 5, c = 15, d = 46;
String aout = "", bout = "", cout="", dout="";
//aout = "00"
//bout = "05"
//cout = "15"
//dout = "46"
When i was looking at int to fixed string precision, it was mostly when dealing with decimals and not prepended padding.
My original thought is that I could do something related to sprintf, such as:
String out = sprintf("%02d", a);
but that didnt seem to work, mostly because It was saying that i am getting a nosuchmethod error. I was not sure if sprintf is in a different package other than core, as i thought this would be related directly to strings.

There's a String.padLeft method you can use:
String out = a.toString().padLeft(2, '0');

Related

How to convert a unicode hex string into its ASCII equivalent

I hope everything is going well.
I have this unicodestring:
353135313531353135313531
And I want to transform it into another unicodestring with this content:
515151515151
In other words, convert a hex representation into its ASCII interpretation.
It is very straightforward to do this in C, but the idea is to work with C++ Builder.
This is what I have been trying to do:
String hex_to_ascii(const String& hex_str) {
String ascii_str = "";
for (int i = 1; i <= hex_str.Length(); i += 2) {
String hex_char = hex_str.SubString(i, 2);
int ascii_char = hex_char.ToInt();
// ascii_str += String().sprintf(_D("%c"), ascii_char);
ascii_str.Insert(ascii_char, ascii_str.Length() + 1);
}
return ascii_str;
But no luck so far.
I know there is a method called ToHex I've been trying to search for documentation about it because it's related to what I am trying to do, so probably the library that has this method has also something close to what I need.
If you know how to do this or where can I read about the ToHex method, please let me know. Thank you for reading.
The code you have is very close, it just needs some minor tweaks.
Most importantly, String::ToInt() WILL NOT decode hex, like you are expecting. It will convert "35" to an integer with a value of decimal 35 (NOT hex 0x35, decimal 53), and will convert "31" to an integer with a value of decimal 31 (NOT hex 0x31, decimal 49), etc.
You need to instead use Sysutils::StrToInt() with a 0x hex prefix prepended to the string value.
Try this:
String hex_to_ascii(const String& hex_str) {
String ascii_str;
for (int i = 1; i <= hex_str.Length(); i += 2) {
String hex_char = _D("0x") + hex_str.SubString(i, 2);
int ascii_char = StrToInt(hex_char);
ascii_str += static_cast<Char>(ascii_char);
}
return ascii_str;
}
Alternatively, you can use HexToBin() to decode the hex into a byte array, and then construct a UnicodeString from those bytes, eg:
String hex_to_ascii(const String& hex_str) {
TBytes bytes;
bytes.Length = hex_str.Length() / 2;
HexToBin(hex_str.c_str(), &bytes[0], bytes.Length);
return String((char*)&bytes[0], bytes.Length);
// Alternatively:
// return TEncoding::Default.GetString(bytes);
}

Sorting Big Int value in Swift

I have a problem to sort some numbers, which are string first. The numbers are too huge for UInt64, so i converted the string numbers to float and then sorted it. That works out great. But then i need to print these numbers with no decimals. So I tried to format the numbers. But the Bigger number are changing its value after formatting it.
Here is the Input array to sort -
["6","31415926535897932384626433832795","1","3","10","3","5"]
And I need to print output in exactly this format -
1
3
3
5
10
31415926535897932384626433832795
Here is my code in swift -
import Foundation
var a = Array<Float>()
var b = ["6","31415926535897932384626433832795","1","3","10","3","5"]
a = b.map{ Float($0)! }
for i in 0..<(a.count-1){
var min = i
for j in (i+1)..<a.count{
if a[j] < a[min] {
min = j
}
}
var temp = a[i]
a[i] = a[min]
a[min] = temp
}
for val in a{
print(String(format: "%.0f",val.rounded(.down)))
}
My Output is -
1
3
3
5
6
10
31415927314585224784361549725696
If you notice the last biggest number is changed from the original input. Any suggestions would be much appreciated! Thanks!
You can use numeric comparison:
let values = ["6","31415926535897932384626433832795","1","3","10","3","5"]
let sortedValues = values.sorted { (value1, value2) in
let order = value1.compare(value2, options: .numeric)
return order == .orderedAscending
}
print(sortedValues) // ["1", "3", "3", "5", "6", "10", "31415926535897932384626433832795"]
With using float, you may get unexpected result for orders between big ints:
var str = "Hello, playground"
let inArray = [
"6",
"31415926535897932384626433832797",
"31415926535897932384626433832796",
"31415926535897932384626433832795",
"1",
"3"
]
let outArray = inArray.sorted {Float($0)! < Float($1)!}
print(outArray)
//->["1", "3", "6", "31415926535897932384626433832797", "31415926535897932384626433832796", "31415926535897932384626433832795"]
As described in mag_zbc's answer, Float has about only 7 significant digits, so all less significant digits are lost.
Float("31415926535897932384626433832797") == Float("31415926535897932384626433832795")
//->true
If the digits of your numbers are 38 at most, you can use Decimal (as also suggested in mag_zbc's answer).
let outWithDecimal = inArray.sorted {Decimal(string: $0)! < Decimal(string: $1)!}
print(outWithDecimal)
//->["1", "3", "6", "31415926535897932384626433832795", "31415926535897932384626433832796", "31415926535897932384626433832797"]
Or else, if your data contains numbers with more than 38 digits, String comparison would work with a little pre-processing:
(Assuming all your numbers are non-negative integer.)
extension String {
func fillZeroLeft(_ length: Int) -> String {
if self.characters.count < length {
return String.init(repeating: "0", count: length-self.characters.count) + self
} else {
return self
}
}
}
let maxLen = inArray.lazy.map{$0.characters.count}.max()!
let outWithString = inArray.sorted {$0.fillZeroLeft(maxLen) < $1.fillZeroLeft(maxLen)}
print(outWithString)
//->["1", "3", "6", "31415926535897932384626433832795", "31415926535897932384626433832796", "31415926535897932384626433832797"]
(But numeric comparison in Sulthan's answer seems to be better for me!)
It's impossible to accurately represent such big numbers using primitive types in Swift.
Your value is ~3e+32
UInt64 has a max value of ~1,8e+18
Float uses 24 bits for a significant, which allows to accurately represent a number up to ~1.6e+8
Double uses 53 bits for significant, which allows to accurately represent a number up to ~9e+15
Floating point types (Float and Double) are able to hold much greater values, but they will lose accuracy above the numbers their significants can hold.
Apart from using some external library, I'd suggest using NSDecimalNumber class, which is supposed to be able to hold up to 38 decimal digits - which is enough for numbers in your example. However, if you need even bigger numbers, then you'll need to look for some external library.

Writing a string to disk without any character conversion in Groovy

I have a String object, now because it comes a diff of a folder containing different file types, not everything in it is encoded in the same character set.
The correct codes are in the string, but whenever I try to access the string, groovy tries to be helpful and decode the string, which messes things up.
Now the following seems to do what I need
String decoded_diff = "String that contains codes from different character encodings"
patch_file_name = 'changes.patch'
patch_file = new File(pwd(), patch_file_name)
patch_file.delete()
max_block_size = 1024 * 1024
char[] char_buffer = new char[max_block_size]
block_start = 0
patch_length = decoded_diff.length()
while (true) {
block_size = Math.min(patch_length - block_start, max_block_size)
decoded_diff.getChars(block_start, block_start + block_size, char_buffer, 0)
block_start += block_size
byte[] byte_buffer = new byte[block_size]
for (int i = 0; i < block_size; i++) {
byte_buffer[i] = (int) char_buffer[i]
}
patch_file.append(byte_buffer)
if (block_start == patch_length) break
}
However, it is sloooow
Is there a faster way to achieve the same thing? The final patch file must be identical to the original diff to work. Unfortunately I can't send the file itself (jenkins currently doesn't support file parameters in pipeline jobs) so I have to escape it and send it as part of a json parameter list, hence this painful rigmarole on the receiving end.
Why not:
String decoded_diff = "String that contains codes from different character encodings"
patch_file_name = 'changes.patch'
patch_file = new File(pwd(), patch_file_name)
patch_file.delete()
patchFile.withOutputStream { os ->
os << decoded_diff.bytes
}

Rounding Currency String

I was able to write this function as an extension method of NSDecimalNumber which worked out pretty well for me. It takes some number and formats it to the currency string. If it contains .00, it will truncate it, but if it's something like .23 or .45, it will retain the decimals.
func toCurrencyString() -> String
{
let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.CurrencyStyle
//Determine whether to show decimals or not (if trailing zeros exist, do not show)
if (trunc(self.floatValue) == self.floatValue) {
nf.maximumFractionDigits = 0
} else {
nf.maximumFractionDigits = 2
}
return nf.stringFromNumber(self)
}
I had a question:
How could implement a similar function above that returns a formatted string, but rounds it up? So something like 45.55 would be 46 (no decimals)
I tried using round() which works in playground, but when I use it extension methods, it says 'ambiguous use of round()'.
also..out of curiosity, is the above safe for different locales like it shows up for me perfectly in America, but if the user's phone was in the UK would it automatically work?
Thanks so much!
Took me a little bit but I got it:
func toRoundedCurrencyString() -> String
{
let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.CurrencyStyle
nf.maximumFractionDigits = 0
var roundingStyle = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundBankers, scale: 0, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false)
var roundedNumber = self.decimalNumberByRoundingAccordingToBehavior(roundingStyle)
return nf.stringFromNumber(roundedNumber)
}
Works well, but concerned with others said. I'm using NSDecimalNumber extension methods. Isn't NSDecimalNumber the preferred way to handle currency? btw i'm using currencies < 100,000 if that matters? Is it only larger currencies where it's a big deal? I'm always using up to 2 decimal places, if even.

Is there a built-in method to pad a string?

I'm trying to add zero-padding to a number. I did some searching and can't find an obvious way to do this. Am I missing something?
Dart 1.3 introduced a String.padLeft and a String.padRight you can use :
String intToString(int i, {int pad 0}) => i.toString().padLeft(pad, '0');
You can use the String function .padLeft(Int width, [String = ' '])
https://api.dartlang.org/apidocs/channels/dev/dartdoc-viewer/dart-core.String#id_padLeft
int i = 2;
print(i.toString().padLeft(2, "0"));
result: 02
I don't think you're missing anything, there's a big lack of formatting functions right now. I think the best you can do is something like:
String intToString(int i, {int pad: 0}) {
var str = i.toString();
var paddingToAdd = pad - str.length;
return (paddingToAdd > 0)
? "${new List.filled(paddingToAdd, '0').join('')}$i" : str;
}
Obviously something that took a format string would be much nicer. Feature request?

Resources