Unable to get substring in flutter and wrong string length - flutter

I am working on application to show mobile contact list with initials in a circle, but not getting initial character for some contact names.
In below code, first name is from mobile's contact list and second one I have typed from keyboard.
I am able to get correct length and also first character of the second name, but length for first name is double and also not able to get first character (it gives �).
print("𝙽𝚊𝚐𝚎𝚜𝚑".substring(0,1)); //�
print("𝙽𝚊𝚐𝚎𝚜𝚑".length); //12
print("Nagesh".substring(0,1)); //N
print("Nagesh".length); //6
Thankyou in advance for answering....

You can use this function to use substring with unicode:
subOnCharecter({required String str, required int from, required int to}) {
var runes = str.runes.toList();
String result = '';
for (var i = from; i < to; i++) {
result = result + String.fromCharCode(runes[i]);
}
return result;
}
and you can use it like this:
print(subOnCharecter(str: "𝙽𝚊𝚐𝚎𝚜𝚑", from: 0, to: 2)); // 𝙽𝚊
print(subOnCharecter(str: "Nagesh", from: 0, to: 2)); // Na
you can use this function instead of default substring.

The strings look similar, but they consist of different unicode characters.
Character U+1d67d "𝙽" is not the same as U+004e "N".
You can use str.runes.length to get the number of unicode characters.
A detailed explanation why the string length is different can be found here
example:
void main() {
var mobileStr = "𝙽𝚊𝚐𝚎𝚜𝚑";
var keyboardStr = "Nagesh";
analyzeString(mobileStr);
print("");
analyzeString(keyboardStr);
}
void analyzeString(String s) {
Runes runes = s.runes; // Unicode code-points of this string
var unicodeChars = runes.map((r) => r.toRadixString(16)).toList();
print("String: $s");
print("String length: ${s.length}");
print("Number of runes: ${runes.length}");
print("unicode characters: ${unicodeChars.join(" ")}");
}
// OUTPUT
// String: 𝙽𝚊𝚐𝚎𝚜𝚑
// String length: 12
// Number of runes: 6
// unicode characters: 1d67d 1d68a 1d690 1d68e 1d69c 1d691
// String: Nagesh
// String length: 6
// Number of runes: 6
// unicode characters: 4e 61 67 65 73 68

Related

Getting substring of a string in flutter that contain and emoji can cause a render failure

As part of the app I am shortening a user-given string to 40 characters and appending ellipses at the end if the string is longer than 40 characters. The users are allowed/able to use emoji in their strings.
If a string that is cut off has an emoji at the 40-character mark it causes it to fail to render and renders as a "�".
Is there a way to reliably get a sub-string of a text but without running into this issue?
Current code:
if (useStr.length > 40) {
useStr = useStr.substring(0, 40) + "...";
}
You should use package:characters and truncate strings based on graphemes (human-perceived characters) and not on (UTF-16) code units.
import 'package:characters/characters.dart';
void main() {
final originalString = '\u{1F336}\uFE0F' * 3; // 🌶️🌶️🌶️
const ellipsis = '\u2026';
for (var i = 1; i < 4; i += 1) {
var s = originalString;
var characters = s.chararacters;
if (characters.length > i) {
s = '${characters.take(i)}$ellipsis';
}
print(s);
}
}
which prints:
🌶️…
🌶️🌶️…
🌶️🌶️🌶️

Swift. How to get the previous character?

For example: I have character "b" and I what to get "a", so "a" is the previous character.
let b: Character = "b"
let a: Character = b - 1 // Compilation error
It's actually pretty complicated to get the previous character from Swift's Character type because Character is actually comprised of one or more Unicode.Scalar values. Depending on your needs you could restrict your efforts to just the ASCII characters. Or you could support all characters comprised of a single Unicode scalar. Once you get into characters comprised of multiple Unicode scalars (such as the flag Emojis or various skin toned Emojis) then I'm not even sure what the "previous character" means.
Here is a pair of methods added to a Character extension that can handle ASCII and single-Unicode scalar characters.
extension Character {
var previousASCII: Character? {
if let ascii = asciiValue, ascii > 0 {
return Character(Unicode.Scalar(ascii - 1))
}
return nil
}
var previousScalar: Character? {
if unicodeScalars.count == 1 {
if let scalar = unicodeScalars.first, scalar.value > 0 {
if let prev = Unicode.Scalar(scalar.value - 1) {
return Character(prev)
}
}
}
return nil
}
}
Examples:
let b: Character = "b"
let a = b.previousASCII // Gives Optional("a")
let emoji: Character = "😆"
let previous = emoji.previousScalar // Gives Optional("😅")

flutter how to compare two different format phone numbers

I am trying to find if two phone numbers are same or not (Two same phone number may not be in the same format, as +919998245345 is same as 9998245345 and 99982 45345)
For this, you can use contains() dart string method. I have marked the trailing statement as bold, cos, it applies on the String. Make sure you get the number in String format, or convert it to String and then perform the operation.
Alogrithm
Convert the number to be compared to String, or get the number as String
Remove all the white spaces using this code, your_phone_number_variable.replaceAll(new RegExp(r"\s+"), ""). So that every number should be having no white spaces in between for smooth operation
Use contains() like this, number1.contains(number2)
Code
// this is a comparision between +919998245345 and other numbers
// you can play around and get what you want
void main() {
var _inputPhone = "+919998245345";
var _checkPhone = "9998245345";
var _anotherCheck = "99982 45345";
// checking that white space removal works or not
print(_anotherCheck.replaceAll(new RegExp(r"\s+"), ""));
// I have just removed the spaces from the number which had the white
// space, you can store the value using this code for every data
// for unknown data coming from server side or user side
_anotherCheck = _anotherCheck.replaceAll(new RegExp(r"\s+"), "");
if(_inputPhone.contains(_anotherCheck)){
print('99982 45345 and +919998245345 are same');
}
if(_inputPhone.contains(_checkPhone)){
print('9998245345 and +919998245345 are same');
}
}
Output
9998245345
99982 45345 and +919998245345 are same
9998245345 and +919998245345 are same
void main() {
print(isSame('9998245345', '+91999824 5345'));
}
bool isSame(String number1, String number2) {
number1 = number1.replaceAll(' ', '');
number2 = number2.replaceAll(' ', '');
int len1 = number1.length;
int len2 = number2.length;
number1 = number1.substring(len1-10, len1);
number2 = number2.substring(len2-10, len2);
return number1 == number2;
}
I think this is the simple and best solution to chnage format of phone
String changeFormat(String phone) {
phone = phone.replaceAll(" ", "");
if (phone.startsWith("+")) {
return "0" + phone.substring(3);
} else {
return phone;
}
}

Reliable function to get position of substring in string in Swift

This is working well for English:
public static func posOf(needle: String, haystack: String) -> Int {
return haystack.distance(from: haystack.startIndex, to: (haystack.range(of: needle)?.lowerBound)!)
}
But for foreign characters the returned value is always too small. For example "का" is considered one unit instead of 2.
posOf(needle: "काम", haystack: "वह बीना की खुली कोयला खदान में काम करता था।") // 21
I later use the 21 in NSRange(location:length:) where it needs to be 28 to make NSRange work properly.
A Swift String is a collection of Characters, and each Character
represents an "extended Unicode grapheme cluster".
NSString is a collection of UTF-16 code units.
Example:
print("का".characters.count) // 1
print(("का" as NSString).length) // 2
Swift String ranges are represented as Range<String.Index>,
and NSString ranges are represented as NSRange.
Your function counts the number of Characters from the start
of the haystack to the start of the needle, and that is different
from the number of UTF-16 code points.
If you need a "NSRange compatible"
character count then the easiest method would be use the
range(of:) method of NSString:
let haystack = "वह बीना की खुली कोयला खदान में काम करता था।"
let needle = "काम"
if let range = haystack.range(of: needle) {
let pos = haystack.distance(from: haystack.startIndex, to: range.lowerBound)
print(pos) // 21
}
let nsRange = (haystack as NSString).range(of: needle)
if nsRange.location != NSNotFound {
print(nsRange.location) // 31
}
Alternatively, use the utf16 view of the Swift string to
count UTF-16 code units:
if let range = haystack.range(of: needle) {
let lower16 = range.lowerBound.samePosition(in: haystack.utf16)
let pos = haystack.utf16.distance(from: haystack.utf16.startIndex, to: lower16)
print(pos) // 31
}
(See for example
NSRange to Range<String.Index> for more methods to convert between Range<String.Index>
and NSRange).

Fit Swift string in database VARCHAR(255)

I'm trying to get a valid substring of at most 255 UTF8 code units from a Swift string (the idea is to be able to store it an a database VARCHAR(255) field).
The standard way of getting a substring is this :
let string: String = "Hello world!"
let startIndex = string.startIndex
let endIndex = string.startIndex.advancedBy(255, limit: string.endIndex)
let databaseSubstring1 = string[startIndex..<endIndex]
But obviously that would give me a string of 255 characters that may require more than 255 bytes in UTF8 representation.
For UTF8 I can write this :
let utf8StartIndex = string.utf8.startIndex
let utf8EndIndex = utf8StartIndex.advancedBy(255, limit: string.utf8.endIndex)
let databaseSubstringUTF8View = name.utf8[utf8StartIndex..<utf8EndIndex]
let databaseSubstring2 = String(databaseSubstringUTF8View)
But I run the risk of having half a character at the end, which means my UTF8View would not be a valid UTF8 sequence.
And as expected databaseSubstring2 is an optional string because the initializer can fail (it is defined as public init?(_ utf8: String.UTF8View)).
So I need some way of stripping invalid UTF8 code points at the end, or – if possible – a builtin way of doing what I'm trying to do here.
EDIT
Turns out that databases understand characters, so I should not try to count UTF8 code units, but rather how many characters the database will count in my string (which will probably depend on the database).
According to #OOPer, MySQL counts characters as UTF-16 code units. I have come up with the following implementation :
private func databaseStringForString(string: String, maxLength: Int = 255) -> String
{
// Start by clipping to 255 characters
let startIndex = string.startIndex
let endIndex = startIndex.advancedBy(maxLength, limit: string.endIndex)
var string = string[startIndex..<endIndex]
// Remove characters from the end one by one until we have less than
// the maximum number of UTF-16 code units
while (string.utf16.count > maxLength) {
let startIndex = string.startIndex
let endIndex = string.endIndex.advancedBy(-1, limit: startIndex)
string = string[startIndex..<endIndex]
}
return string
}
The idea is to count UTF-16 code units, but remove characters from the end (that is what Swift think what a character is).
EDIT 2
Still according to #OOPer, Posgresql counts characters as unicode scalars, so this should probably work :
private func databaseStringForString(string: String, maxLength: Int = 255) -> String
{
// Start by clipping to 255 characters
let startIndex = string.startIndex
let endIndex = startIndex.advancedBy(maxLength, limit: string.endIndex)
var string = string[startIndex..<endIndex]
// Remove characters from the end one by one until we have less than
// the maximum number of Unicode Scalars
while (string.unicodeScalars.count > maxLength) {
let startIndex = string.startIndex
let endIndex = string.endIndex.advancedBy(-1, limit: startIndex)
string = string[startIndex..<endIndex]
}
return string
}
As I write in my comment, you may need your databaseStringForString(_:maxLength:) to truncate your string to match the length limit of your DBMS. PostgreSQL with utf8, MySQL with utf8mb4.
And I would write the same functionality as your EDIT 2:
func databaseStringForString(string: String, maxUnicodeScalarLength: Int = 255) -> String {
let start = string.startIndex
for index in start..<string.endIndex {
if string[start..<index.successor()].unicodeScalars.count > maxUnicodeScalarLength {
return string[start..<index]
}
}
return string
}
This may be less efficient, but a little bit shorter.
let s = "abc\u{1D122}\u{1F1EF}\u{1F1F5}" //->"abc𝄢🇯🇵"
let dbus = databaseStringForString(s, maxUnicodeScalarLength: 5) //->"abc𝄢"(=="abc\u{1D122}")
So, someone who works with MySQL with utf8(=utf8mb3) needs something like this:
func databaseStringForString(string: String, maxUTF16Length: Int = 255) -> String {
let start = string.startIndex
for index in start..<string.endIndex {
if string[start..<index.successor()].utf16.count > maxUTF16Length {
return string[start..<index]
}
}
return string
}
let dbu16 = databaseStringForString(s, maxUTF16Length: 4) //->"abc"