How to read data from a big txt file in dart - flutter

When i read data from a big txt file block by block ,I got the error as blow:
Unfinished UTF-8 octet sequence (at offset 4096)
code:
File file = File(path!);
RandomAccessFile _raf = await file.open();
_raf.setPositionSync(skip ?? 0);
var data = _raf.readSync(block);// block = 64*64
content.value = utf8.decode(data.toList());

UTF*8 is variable length encoding.
The error come from data not align to UTF8 boundary
Alternative way is to trim data byte on left and right before call utf.decode
This will lost first and last character. You may read and add more bytes to cover last character and align with utf8 boundary
bool isDataByte(int i) {
return i & 0xc0 == 0x80;
}
Future<void> main(List<String> arguments) async {
var _raf = await File('utf8.txt').open();
_raf.setPositionSync(skip);
var data = _raf.readSync(8 * 8);
var utfData = data.toList();
int l, r;
for (l = 0; isDataByte(utfData[l]) && l < utfData.length; l++) {}
for (r = utfData.length - 1; isDataByte(utfData[r]) && r > l; r--) {}
var value = utf8.decode(utfData.sublist(l, r));
print(value);
}
Optional read more 4 bytes and expand to cover last character
bool isDataByte(int i) {
return i & 0xc0 == 0x80;
}
Future<void> main(List<String> arguments) async {
var _raf = await File('utf8.txt').open();
_raf.setPositionSync(skip);
var block = 8 * 8;
var data = _raf.readSync(block + 4);
var utfData = data.toList();
int l, r;
for (l = 0; isDataByte(utfData[l]) && l < block; l++) {}
for (r = block; isDataByte(utfData[r]) && r < block + 4; r++) {}
var value = utf8.decode(utfData.sublist(l, r));
print(value);
}

Related

How to send data greatter than 512 bytes using flutter_blue?

I have a file that is larger than 512 bytes to send to an esp32. I'm sending "withoutResponse: false", the flutter_blue library does the split according to the mtu size without problems, but when it reaches 512 bytes it returns an error to write in characteristic. To solve this I have a function that splits the file and writes each 512 bytes.
Esp32 can send me files larger than 512 without doing anything. Can I send larger files without splitting?
Example of code or library that makes this possible
ok, I decided to switch to the flutter_reactive_ble library, and I developed code similar to this one. Basically I split the file into smaller packets of 4096 bytes (this was defined in my communication protocol with esp32), then I call the function that sends and it splits again the file into packets the size of mtu less 19 bytes that are from the header. In the last packet I "writeWithResponse" and wait for the response, if it responds ok, increment and send the next package and repit the processs until the end of file.
late StreamSubscription<List<int>>? subscribeStream;
List<int> bytesOfFile = [];
int _size = 0;
int _increment = 0;
List<int> _returnOfSubscribe = [];
int _indexOfUploadController = 0;
Future<void> subscribeCharacteristic() async {
subscribeStream = widget
.subscribeToCharacteristic(widget.rcharacteristic)
.listen((event) {
debugPrint("resposta${hexToString(event)}");
setState(() {
_returnOfSubscribe = event;
if (_returnOfSubscribe != 'code of confirm reception file') {
debugPrint('err');
} else {
_increment++;
actualize();
}
});
});
}
void actualize() async {
int splitSize = 4096;
Iterable<int> s;
int n = (bytesOfFile.length / splitSize).ceil();
//if is the last split
if (_increment >= n) {
if (_returnOfSubscribe == 'code of confirm end of file') {
debugPrint("success");
} else {
debugPrint("err");
}
return;
}
if ((_size + splitSize) < bytesOfFile.length) {
s = bytesOfFile.getRange(_size, _size + splitSize);
} else {
s = bytesOfFile.getRange(_size, bytesOfFile.length);
}
await writeLongData(s.toList());
_size += splitSize;
}
writeLongData(List<int> data) async {
int splitSize = mtuNotifier.value - 19;
Iterable<int> s;
int size = 0;
int n = (data.length / splitSize).ceil();
for (int i = 0; i < n; i++) {
if ((size + splitSize) < data.length) {
s = data.getRange(size, size + splitSize);
} else {
s = data.getRange(size, data.length);
}
try {
if ((size + splitSize) < data.length) {
await widget.writeWithoutResponse(widget.wcharacteristic, s.toList());
} else {
//if the last, write with response
await widget.writeWithResponse(widget.wcharacteristic, s.toList());
}
} catch (e) {
debugPrint('$e');
return;
}
size += splitSize;
}
}

split the string into equal parts flutter

There is a string with random numbers and letters. I need to divide this string into 5 parts. And get List. How to do it? Thanks.
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
Should work:
List<String> list = [
'05b37ffe',
'4973959c',
'4d4f2d5c',
'a0c14357',
'49f8cc66',
];
I know there'a already a working answer but I had already started this so here's a different solution.
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
List<String> list = [];
final divisionIndex = str.length ~/ 5;
for (int i = 0; i < str.length; i++) {
if (i % divisionIndex == 0) {
final tempString = str.substring(i, i + divisionIndex);
list.add(tempString);
}
}
log(list.toString()); // [05b37ffe, 4973959c, 4d4f2d5c, a0c14357, 49f8cc66]
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
int d=1
; try{
d = (str.length/5).toInt();
print(d);
}catch(e){
d=1;
}
List datas=[];
for(int i=0;i<d;i++){
var c=i+1;
try {
datas.add(str.substring(i * d, d*c));
} catch (e) {
print(e);
}
}
print(datas);
}
OR
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
int d = (str.length / 5).toInt();
var data = List.generate(d - 3, (i) => (d * (i + 1)) <= str.length ? str.substring(i * d, d * (i + 1)) : "");
print(data);//[05b37ffe, 4973959c, 4d4f2d5c, a0c14357, 49f8cc66]
If you're into one liners, with dynamic parts.
Make sure to import dart:math for min function.
This is modular, i.e. you can pass whichever number of parts you want (default 5). If you string is 3 char long, and you want 5 parts, then it'll return 3 parts with 1 char in each.
List<String> splitIntoEqualParts(String str, [int parts = 5]) {
int _parts = min(str.length, parts);
int _sublength = (str.length / _parts).ceil();
return Iterable<int>
//Initialize empty list
.generate(_parts)
.toList()
// Apply the access logic
.map((index) => str.substring(_sublength * index, min(_sublength * index + _sublength, str.length)))
.toList();
}
You can then use it such as print(splitIntoEqualParts('05b37ffe4973959c4d4f2d5ca0c1435749f8cc66', 5));
splitWithCount(String string,int splitCount)
{
var array = [];
for(var i =0 ;i<=(string.length-splitCount);i+=splitCount)
{
var start = i;
var temp = string.substring(start,start+splitCount);
array.add(temp);
}
print(array);
}

ethereum solidity: from concatenated hexadecimal strings to uint64[]

I want my API to return an array of uint64 to my on-chain contract.
I tried 2 response formats for my API:
The array of uint64 itself (BN string here, but I need it in true uint64 not strings in my contract):
{"data":["629343835796877311","629343835797458943","629343835797471231"]}
concatenated hexadecimal strings (a new value every 16 chars):
{"data":"08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff"}
I discarded using the first approach because having ["629343835796877311","629343835797458943","629343835797471231"] as bytes is actually difficult to extract. I might be wrong! Maybe there is a base64 approach to encode and decode the data back into solidity data types, maybe?
I will use the second approach bellow.
Chainlink will pass the response as bytes memory _data:
function fulfill(bytes32 _requestId, bytes memory _data)
public
recordChainlinkFulfillment(_requestId)
{
data = string(_data);
}
Those bytes memory _data are successfully received and converted to a string (in storage data). The string value looks like this
08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff ...
In this example each 16 chars represent a uint64 number.
The first one: 08bbe0e25e412fff is 629343835796877311 for instance.
In solidity, I need to split the string each 16 chars and then convert it into their uint64 value.
I could use the bytes memory _data instead of the string(_data) if the code would be simpler or consume less gas. I am not sure
Please I need help with this I have been struggling.
Thanks
I got this contract working
COMMENTS:
the method hexBytesToInt is going to get a string representing hexa value like ffa0 for instance and return it's decimal value.
the method getSlice is just going to slice a string. In my case I have a new hexa value every 16 chars so I need to slice (0,16) than (16,32) etc...
the method hexStringToIntArray is managing the increments to slice every 16 chars and call the hexBytesToInt to transform the hex string in uint.
If you really want to dig into this solution, you are better off starting by understanding the test cases.
pragma solidity >=0.4.22 <0.8.11;
contract Serializer {
function hexStringToIntArray(string memory s) public pure returns (uint64[] memory) {
uint size = bytes(s).length / 16;
uint64[] memory result = new uint64[](size);
for (uint i = 0; i< size; i++) {
string memory strSlice = getSlice(i*16, (i+1)*16, s);
result[i] = hexStringToInt(strSlice);
}
return result;
}
function getSlice(uint startIndex, uint endIndex, string memory str) public pure returns (string memory) {
bytes memory strBytes = bytes(str);
bytes memory result = new bytes(endIndex-startIndex);
for(uint i = startIndex; i < endIndex; i++) {
result[i-startIndex] = strBytes[i];
}
return string(result);
}
function hexBytesToInt(bytes memory ss) public pure returns (uint64){
uint64 val = 0;
uint8 a = uint8(97); // a
uint8 zero = uint8(48); //0
uint8 nine = uint8(57); //9
uint8 A = uint8(65); //A
uint8 F = uint8(70); //F
uint8 f = uint8(102); //f
for (uint i=0; i<ss.length; ++i) {
uint8 byt = uint8(ss[i]);
if (byt >= zero && byt <= nine) byt = byt - zero;
else if (byt >= a && byt <= f) byt = byt - a + 10;
else if (byt >= A && byt <= F) byt = byt - A + 10;
val = (val << 4) | (byt & 0xF);
}
return val;
}
function hexStringToInt(string memory s) public pure returns (uint64) {
bytes memory ss = bytes(s);
uint64 val = hexBytesToInt(ss);
return val;
}
}
the tests:
const Serializer = artifacts.require("Serializer");
const truffleAssert = require("truffle-assertions");
const fs = require("fs");
const { readLines } = require("./utils.js");
const BN = web3.utils.BN;
contract("Serializer", (accounts) => {
const [deployerAddress, tokenHolderOneAddress, tokenHolderTwoAddress] = accounts;
it("hexStringToInt", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToInt.call("08bbe0e25e412fff");
let expected = new BN("629343835796877311");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a0fff");
expected = new BN("629343835797458943");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a3fff");
expected = new BN("629343835797471231");
assert.equal(result.toString(10), expected.toString(10));
});
it("getSlice1", async () => {
let s = await Serializer.deployed();
let result = await s.getSlice.call(0, 16, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
let expected = "08bbe0e25e412fff";
assert.equal(result, expected);
});
it("getSlice2", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(16, 32, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a0fff";
assert.equal(result, expected);
});
it("getSlice3", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(32, 48, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a3fff";
assert.equal(result, expected);
});
it("hexStringToIntArray", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToIntArray.call("08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
console.log(result);
let expected = [
new BN("629343835796877311").toString(),
new BN("629343835797458943").toString(),
new BN("629343835797471231").toString(),
];
const resultS = result.map((x) => x.toString());
assert.deepEqual(resultS, expected);
});
});

How to convert double into string with 2 significant digits?

So i have small double values and i need to convert them into string in order to display in my app. But i care only about first two significant digits.
It should work like this:
convert(0.000000000003214324) = '0.0000000000032';
convert(0.000003415303) = '0.0000034';
We can convert double to string, then check every index and take up to two nonzero (also .) strings. But the issue comes on scientific notation for long double.
You can check Convert long double to string without scientific notation (Dart)
We need to find exact String value in this case. I'm taking help from this answer.
String convert(String number) {
String result = '';
int maxNonZeroDigit = 2;
for (int i = 0; maxNonZeroDigit > 0 && i < number.length; i++) {
result += (number[i]);
if (number[i] != '0' && number[i] != '.') {
maxNonZeroDigit -= 1;
}
}
return result;
}
String toExact(double value) {
var sign = "";
if (value < 0) {
value = -value;
sign = "-";
}
var string = value.toString();
var e = string.lastIndexOf('e');
if (e < 0) return "$sign$string";
assert(string.indexOf('.') == 1);
var offset =
int.parse(string.substring(e + (string.startsWith('-', e + 1) ? 1 : 2)));
var digits = string.substring(0, 1) + string.substring(2, e);
if (offset < 0) {
return "${sign}0.${"0" * ~offset}$digits";
}
if (offset > 0) {
if (offset >= digits.length) return sign + digits.padRight(offset + 1, "0");
return "$sign${digits.substring(0, offset + 1)}"
".${digits.substring(offset + 1)}";
}
return digits;
}
void main() {
final num1 = 0.000000000003214324;
final num2 = 0.000003415303;
final v1 = convert(toExact(num1));
final v2 = convert(toExact(num2));
print("num 1 $v1 num2 $v2");
}
Run on dartPad

Convert std::string from UTF8, UTF16, ISO88591 to Hexadecimal

I try to Encoding the std::string from UTF8,... to Hexadecimal. What I can't do right now is that I can't get the decimal value of each character of the input string to convert it if the input string contains special character which is from the Code Page Identifiers(windows-1258) include Vietnamese character.
At first, I will get the decimal value and then convert it to Binary and then to Hexadecimal. s is my input string. s = "Ồ".
void StringUtils::strToBinary(wstring s, string* result)
{
int n = s.length();
for (int i = 0; i < n; i++)
{
wchar_t c = s[i];
long val = long(c);
std::string bin = "";
while (val > 0)
{
(val % 2) ? bin.push_back('1') :
bin.push_back('0');
val /= 2;
}
reverse(bin.begin(), bin.end());
result->append(convertBinToHex(bin));
}
}
std::string StringUtils::convertBinToHex(std::string temp) {
long long binaryValue = atoll(temp.c_str());
long long dec_value = 0;
int base = 1;
int i = 0;
while (binaryValue) {
long long last_digit = binaryValue % 10;
binaryValue = binaryValue / 10;
dec_value += last_digit * base;
base = base * 2;
}
char hexaDeciNum[10];
while (dec_value != 0)
{
int temp = 0;
temp = dec_value % 16;
if (temp < 10)
{
hexaDeciNum[i] = temp + 48;
i++;
}
else
{
hexaDeciNum[i] = temp + 55;
i++;
}
dec_value = dec_value / 16;
}
std::string str;
for (int j = i - 1; j >= 0; j--) {
str = str + hexaDeciNum[j];
}
return str;
}
If my input only contain "Ồ" this is what my expected output
UTF8 : E1BB92
UTF16 : FEFF 1ED2
UTF16BE : 1ED2
UTF16LE : D21E
This how I do it in Java
Charset charset = Charset.forName(Enum.valueOf(Encoding.class, encodingType).toString());
ByteBuffer buffer = charset.newEncoder().encode(CharBuffer.wrap(inputString.toCharArray()));
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes, 0, buffer.limit());
result = new ByteField(bytes);
return result;
}