This may be simple in other languages but I can't figure out how to do it in Solidity.
I have a bytes32 like this 0x05416460deb76d57af601be17e777b93592d8d4d4a4096c57876a91c84f4a712.
I don't want to convert the bytes to a string, rather I just want to represent the whole thing as a string, like "0x05416460deb76d57af601be17e777b93592d8d4d4a4096c57876a91c84f4a712".
How can this be done in Solidity?
Update:
Why I need to do this: Basically I connect to an oracle, which does some work off-chain and finally uploads a file to IPFS. I need to get the content identifier into my contract from the oracle. The oracle can only send bytes32 as a response, so I convert it to a multihash and send only the digest as bytes32 from oracle to contract.
So far so good, I can recreate the multihash in my contract. The problem is that after this I create an ERC721 (NFT) token and I have to store some reference to the IPFS file in the metadata, which can only be in string format. This is where I'm stuck at the moment.
While the answer from #Burt looks correct (didn't test it though), there is a much more efficient way to solve the same task:
function toHex16 (bytes16 data) internal pure returns (bytes32 result) {
result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
(bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
(result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
(result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
(result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
(result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
uint256 (result) +
(uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);
}
function toHex (bytes32 data) public pure returns (string memory) {
return string (abi.encodePacked ("0x", toHex16 (bytes16 (data)), toHex16 (bytes16 (data << 128))));
}
This code produces upper case output. For lower case output, just change 7 to 39 in the code.
Explanation
The idea is to process 16 bytes at once using binary operations.
The toHex16 function converts a sequence of 16 bytes represented as a bytes16 value into a sequence of 32 hexadecimal digits represented as a bytes32 value. The toHex function splits a bytes32 value into two bytes16 chunks, converts each chunk to hexadecimal representation via the toHex16 function, and finally concatenates the 0x prefix with the converted chunks using abi.encodePacked function.
The most sophisticated part is how the toHex16 function works. Let's explain it sentence by sentence.
The first sentence:
result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
(bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
Here we shift the last 64 bits of the input to the right by 64 bits, basically doing:
0123456789abcdeffedcba9876543210
\______________/\______________/
| |
| +---------------+
______V_______ ______V_______
/ \ / \
0123456789abcdef0000000000000000fedcba9876543210
The second sentence:
result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
(result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
Here we shift the last 32 bits of both 64-bit chunks to the right by 32 bits:
0123456789abcdef0000000000000000fedcba9876543210
\______/\______/ \______/\______/
| | | |
| +-------+ | +-------+
__V___ __V___ __V___ __V___
/ \ / \ / \ / \
012345670000000089abcdef00000000fedcba980000000076543210
The next sentence:
result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
(result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
does:
012345670000000089abcdef00000000fedcba980000000076543210
\__/\__/ \__/\__/ \__/\__/ \__/\__/
| | | | | | | |
| +---+ | +---+ | +---+ | +---+
V_ V_ V_ V_ V_ V_ V_ V_
/ \ / \ / \ / \ / \ / \ / \ / \
012300004567000089ab0000cdef0000fedc0000ba980000765400003210
And the next one:
result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
(result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
does:
012300004567000089ab0000cdef0000fedc0000ba980000765400003210
\/\/ \/\/ \/\/ \/\/ \/\/ \/\/ \/\/ \/\/
| | | | | | | | | | | | | | | |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+
V V V V V V V V V V V V V V V V
/\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\
01002300450067008900ab00cd00ef00fe00dc00ba00980076005400320010
The final sentence in this series is a bit different:
result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
(result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
It shifts odd nibbles to the right by 4 bits, and even nibbles by 8 bits:
01002300450067008900ab00cd00ef00fe00dc00ba00980076005400320010
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
000102030405060708090a0b0c0d0e0f0f0e0d0c0b0a09080706050403020100
So all the nibbles of the initial data are distributed one per byte.
Now with every byte x we need to do the following transformation:
x` = x < 10 ? '0' + x : 'A' + (x - 10)
Let's rewrite this formula a bit:
x` = ('0' + x) + (x < 10 ? 0 : 'A' - '0' - 10)
x` = ('0' + x) + (x < 10 ? 0 : 1) * ('A' - '0' - 10)
Note, that (x < 10 ? 0 : 1) could be calculated as ((x + 6) >> 4), thus we have:
x` = ('0' + x) + ((x + 6) >> 4) * ('A' - '0' - 10)
x` = (0x30 + x) + ((x + 0x06) >> 4) * 7
The final statement:
result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
uint256 (result) +
(uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);
Basically performs the above calculation for every byte. The
0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
mask after the right shift is needed to zero out the bits "dropped" by the right shift in the original formula.
BTW, it would be better to ask questions like this one at https://ethereum.stackexchange.com/
Function bytes32ToString turns a bytes32 to hex string
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
uint8 i = 0;
bytes memory bytesArray = new bytes(64);
for (i = 0; i < bytesArray.length; i++) {
uint8 _f = uint8(_bytes32[i/2] & 0x0f);
uint8 _l = uint8(_bytes32[i/2] >> 4);
bytesArray[i] = toByte(_f);
i = i + 1;
bytesArray[i] = toByte(_l);
}
return string(bytesArray);
}
function toByte(uint8 _uint8) public pure returns (byte) {
if(_uint8 < 10) {
return byte(_uint8 + 48);
} else {
return byte(_uint8 + 87);
}
}
Related
Given {"a": 1234567890}, I want 1,234,567,890 in the result, how this can be done with jq
echo '{"a": 1234567890}' | jq '.a | FORMAT?'
Thanks for #peak's answer, the solution is
echo '{"a": 1234567890}' | jq -r 'def h: [while(length>0; .[:-3]) | .[-3:]] | reverse | join(","); .a | tostring | h'
//-> 1,234,567,890
Here's an idiomatic one-liner definition:
def h: tostring | [while(length>0; .[:-3]) | .[-3:]] | reverse | join(",");
Example
12, 123, 1234, 12345678 | h
Output (using -r option):
12
123
1,234
12,345,678
jq doesn't have (yet) a printf function to format according locale settings.
If that's an option for you can pass the number to the shell using printf:
echo '{"a": 12345}' | jq '.a' | xargs printf "%'.f\n"
12,345
Note that the printf conversion relies on the format %'.f that is explained in man 3 printf
Here's a generic solution for integers or integer-valued strings:
# "h" for "human-readable"
def h:
def hh: .[0] as $s | .[1] as $answer
| if ($s|length) == 0 then $answer
else ((if $answer == "" then "" else "," end) + $answer ) as $a
| [$s[0:-3], $s[-3:] + $a] | hh
end;
[ tostring, ""] | hh;
Example
12, 123, 1234, 12345678 | h
Result (using -r option):
12
123
1,234
12,345,678
I have a comma separated CSV file with headers and want to include them in the table
Input:
header,word1,word2,word3
supercalifragi,black,white,red
adc,bad,cat,love
Output:
| header | word1 | word2 | word3 |
| -------------- | ----- | ----- | ----- |
| supercalifragi | black | white | red |
| adc | bad | cat | love |
I need to include the headers and I need to take into account the length of the words in the input file so that the finished table formats correctly
Here is the updated code:
function pr(){
for(i=1;i<=NF;i++)
printf "| %-"len[i]+1"s",$i;
printf "|\n"
}
NR==FNR{
for(i=1;i<=NF;i++)
if(len[i]<length($i)){
len[i]=length($i);
word[i]=$i
}next
}{pr()}
FNR==1{
for(i=1;i<=NF;i++){
gsub(/./,"-",word[i]);
$i=word[i]};
pr()
}
``
I took the freedom of rewriting the entire code from scratches. This should work:
BEGIN {
FS=","
OFS=" | "
for (i=1; i<=NF; i++) {
transientLength[i] = 0
}
}
{
if(NR==1) {
# read headers
for (i=0; i<NF; i++) {
headers[i] = $(i+1)
transientLength[i] = (length($(i+1))>=transientLength[i] ? length($(i+1)) : transientLength[i])
}
} else {
for (i=0; i<NF; i++) {
fields[NR][i] = $(i+1)
transientLength[i] = (length($(i+1))>=transientLength[i] ? length($(i+1)) : transientLength[i])
}
}
}
END {
# print header
for (j in headers) {
spaceLength = transientLength[j]-length(headers[j])
for (s=1;s<=spaceLength;s++) {
spaces = spaces" "
}
if (!printable) printable = headers[j] spaces
else printable = printable OFS headers[j] spaces
spaces = "" # garbage collection
}
printable = "| "printable" |"
print printable
printable = "" # garbage collection
# print alignments
for (j in transientLength) {
for (i=1;i<=transientLength[j];i++) {
sep = sep"-"
}
if (!printable) printable = sep
else printable = printable OFS sep
sep = "" # garbage collection
}
printable = "| "printable" |"
print printable
printable = "" # garbage collection
# print all rows
for (f in fields) {
for (j in fields[f]) {
spaceLength = transientLength[j]-length(fields[f][j])
for (s=1;s<=spaceLength;s++) {
spaces = spaces" "
}
if (!printable) printable = fields[f][j] spaces
else printable = printable OFS fields[f][j] spaces
spaces = "" # garbage collection
}
printable = "| "printable" |"
print printable
printable = "" # garbage collection
}
}
But please be aware: you need to clean your input file of unnecessary whitespaces. It should read:
header,word1,word2,word3
supercalifragi,black,white,red
adc,bad,cat,love
Alternatively, you might use FS=", ", but that would be actually limited to your example.
a shorter alternative with double scanning
$ awk -F' *, *' 'function pr()
{for(i=1;i<=NF;i++) printf "| %-"len[i]+1"s",$i; printf "|\n"}
NR==FNR{for(i=1;i<=NF;i++)
if(len[i]<length($i)) {len[i]=length($i); word[i]=$i} next}
{pr()}
FNR==1{for(i=1;i<=NF;i++) {gsub(/./,"-",word[i]); $i=word[i]}; pr()}' file{,}
| header | word1 | word2 | word3 |
| -------------- | ----- | ----- | ----- |
| supercalifragi | black | white | red |
| adc | bad | cat | love |
It's not exactly the output you asked for but maybe this is all you really need:
$ column -t -s, -o' | ' < file | awk '1; NR==1{gsub(/[^|]/,"-"); print}'
header | word1 | word2 | word3
---------------|-------|-------|------
supercalifragi | black | white | red
adc | bad | cat | love
If you look at the source code of some HTML sites, they use some giant alphabets to comment out or to provide their site name. Something where you write the word and it converts to to HTML like below:
<!--
------
-
---
-
------
!-->
anyone know how to create these types of comments?
Just Google "Big ASCII text art" and you'll find several online generators.
For example, here's one from bigtext.org:
_ _
/ \ __| | __ _ _ __ ___
/ _ \ / _` |/ _` | '_ ` _ \
/ ___ \ (_| | (_| | | | | | |
/_/ \_\__,_|\__,_|_| |_| |_|
Here's another website that lets you customize the font and a few other things. Example (using the font 'tinker-toy'):
O o
/ \ |
o---o o-O oo o-O-o
| || | | | | | |
o o o-o o-o-o o o
I have a table (say UserInputDetails) with the following entries:
+------------+-----------+----------+
| screenId | userInput | numInput |
+------------+-----------+----------+
| 13_1_2_1 | 2 | 9 |
| 13_1_2_2 | 2 | 9 |
| 13_1_2_2 | 3 | 2 |
| 13_1_2_2 | 9 | 2 |
| 13_1_2_2_2 | 3 | 3 |
| 13_1_2_2_2 | 5 | 2 |
| 13_2_2_2 | 4 | 4 |
| 13_2_2_2 | 5 | 4 |
| 13_2_2_2 | 7 | 2 |
+------------+-----------+----------+
I need to write a shell script which gives its expected output as:
13_1_2_1,0,0,9,0,0,0,0,0,0,0
13_1_2_2,0,0,9,2,0,0,0,0,0,2
13_1_2_2_2,0,0,0,3,0,2,0,0,0,0
13_2_2_2,0,0,0,0,4,4,0,2,0,0
Explanation for the output:
the first line of input denotes the numInputs for a particular userInput for screenId '13_1_2_1'. The line first prints the screenId and then corresponding NumInput for userInput 0-9. Since the numInput for userInput '2' is 9 and for the rest of 0-9 is 0, it gives the value 13_1_2_1,0,0,9,0,0,0,0,0,0,0
The bash script written for the following function is:
#!/bin/bash
MYSQL="mysql -uroot -proot -N Database1"
yesterday=""
if [ $# -ge 1 ]
then
yesterday="$1"
else
yesterday=`$MYSQL -sBe "select date_sub(date(now()), interval 1 day);"`
fi
echo "DATE: $yesterday"
PREVSCREENID=''
SCREENID=
ABC=tempSqlDataFile
$MYSQL -sBe "select screenId, userInput, numInput from userInputDetails group by screenID, userInput" > $ABC
for i in {0..9}
do
arr[$i]='0'
done
while read line
do
SCREENID=`echo $line | awk '{ print $1 }'`
i=`echo $line | awk '{print $2 }'`
arr[$i]=`echo $line | awk '{print $3}'`
if [[ $SCREENID != $PREVSCREENID ]]
then
echo "$SCREENID ${arr[*]}" | tr ' ' ','
for i in {0..9}
do
arr[$i]='0'
done
else
i=`echo $line | awk '{print $2 + 1}'`
arr[$i]=`echo $line | awk '{print $3}'`
fi
PREVSCREENID=$SCREENID
done < $ABC
The logic somewhere is going wrong and I am unable to get the logic right. the output from the above shell script is:
13_1_2_1,0,0,9,0,0,0,0,0,0,0,
13_1_2_2,0,0,9,0,0,0,0,0,0,0,
13_1_2_2_2,0,0,9,3,0,0,0,0,0,2,
13_2_2_2,0,0,0,3,4,2,0,0,0,0,
Please can you help me fix the logic in my script? Also, since I am new to scripting and programming, this may not be an efficient way to perform this task. Please suggest if there is an efficient way.
There are a number of errors in your script. Here's a rewrite of the latter part:
while read SCREENID i n; do
if [[ "$SCREENID" != "$PREVSCREENID" ]]; then
[ "$PREVSCREENID" ] && echo "$PREVSCREENID ${arr[*]}" | tr ' ' ,
for j in {0..9}; do arr[$j]=0; done
fi
arr[$i]="$n"
PREVSCREENID="$SCREENID"
done < "$ABC"
echo "$PREVSCREENID ${arr[*]}" | tr ' ' ,
You can avoid calling tr like this:
print_arr() { IFS=,; echo $PREVSCREENID,"${arr[*]}"; unset IFS; }
while read SCREENID i n; do
if [[ "$SCREENID" != "$PREVSCREENID" ]]; then
[ "$PREVSCREENID" ] && print_arr
for j in {0..9}; do arr[$j]=0; done
fi
arr[$i]="$n"
PREVSCREENID="$SCREENID"
done < "$ABC"
print_arr
I want to output the results from a psql query to a csv file. I have used the following approach
\o test.csv
SELECT myo_date, myo_maps_study, cbp_lvef, cbp_rvef, myx_ecg_posneg, myx_st, std_drugs, std_reason_comment FROM myo INNER JOIN studies ON (myo_std_uid = std_uid) LEFT OUTER JOIN cbp on (std_uid = cbp_std_uid) LEFT OUTER JOIN myx on (std_uid = myx_std_uid) WHERE myo_maps_study ~ 'MYO[0-9]*\$' AND std_reason_comment ~ 'AF' AND cbp_lvef is not null AND myx_st IS NOT NULL AND std_drugs IS NOT NULL ORDER by myo_date DESC LIMIT 500;
\q
The results on the query on its own is as follows
06/11/2013 | MYO134537 | 36.75000 | 29.00000 | - | 0.0 | ASPIRIN;BISOPROLOL;LISINOPRIL;METFORMIN;PPI;STATIN;FLUOXETINE;AMLODIPINE;GTN | CPOE;AF;T2DM;POSET
31/10/2013 | MYO130555 | 45.00000 | 36.25000 | - | 0.0 | DILTIAZEM;STATIN;LISINOPRIL;ASPIRIN;FRUSEMIDE;SALBUTAMOL;PARACETAMOL;AMOXICILLIN | TROP-VE; CP; AF; CTPA-VE; ANT T; INV; RF
23/10/2013 | MYO130538 | 18.75000 | 18.50000 | + | -1.0 | ASPIRIN;BISOPROLOL;RAMIPRIL | AF;MR;QLVFN;FAILED CARDIOVERSION
18/10/2013 | MYO134510 | 39.50000 | 32.25000 | - | 0.0 | ASPIRIN;STATIN;CO-CODAMOL;BISOPROLOL;GTN;PPI | PVD;AF
18/10/2013 | MYO130537 | 19.00000 | 18.00000 | - | 0.0 | STATIN;RAMIPRIL;AMLODIPINE;WARFARIN;(METOPROLOL-STOPPED FOR TEST) | TIA;AF;RF+++;ETINAP
However the csv file (opened in open office) looks like this
06/11/2013 MYO134537 36.75 29 -0 0 ASPIRIN;BISOPROLOL;LISINOPRIL;METFORMIN;PPI;STATIN;FLUOXETINE;AMLODIPINE;GTN CPOE;AF;T2DM;POSET
31/10/2013 MYO130555 45 36.25 -0 0 DILTIAZEM;STATIN;LISINOPRIL;ASPIRIN;FRUSEMIDE;SALBUTAMOL;PARACETAMOL;AMOXICILLIN TROP-VE; CP; AF; CTPA-VE; ANT T; INV; RF
23/10/2013 MYO130538 18.75 18.5 0 -1 ASPIRIN;BISOPROLOL;RAMIPRIL AF;MR;QLVFN;FAILED CARDIOVERSION
18/10/2013 MYO134510 39.5 32.25 -0 0 ASPIRIN;STATIN;CO-CODAMOL;BISOPROLOL;GTN;PPI PVD;AF
18/10/2013 MYO130537 19 18 -0 0 STATIN;RAMIPRIL;AMLODIPINE;WARFARIN;(METOPROLOL-STOPPED FOR TEST) TIA;AF;RF+++;ETINAP
The '-' signs have become -0 and '+' have become 0. For clarity, I would like to change these to N and P respectively.
Doing a more test.csv gives
06/11/2013,MYO134537,36.75,29,-0,0,ASPIRIN;BISOPROLOL;LISINOPRIL;METFORMIN;PPI;STATIN;FLUOXETINE;AMLODIPINE;GTN,CPOE;AF;T2DM;POSET,,
31/10/2013,MYO130555,45,36.25,-0,0,DILTIAZEM;STATIN;LISINOPRIL;ASPIRIN;FRUSEMIDE;SALBUTAMOL;PARACETAMOL;AMOXICILLIN,TROP-VE; CP; AF; CTPA-VE; ANT T; INV; RF,,
23/10/2013,MYO130538,18.75,18.5,0,-1,ASPIRIN;BISOPROLOL;RAMIPRIL,AF;MR;QLVFN;FAILED CARDIOVERSION,,
18/10/2013,MYO134510,39.5,32.25,-0,0,ASPIRIN;STATIN;CO-CODAMOL;BISOPROLOL;GTN;PPI,PVD;AF,,
18/10/2013,MYO130537,19,18,-0,0,STATIN;RAMIPRIL;AMLODIPINE;WARFARIN;(METOPROLOL-STOPPED FOR TEST),TIA;AF;RF+++;ETINAP,,
However, when I select the cell in open office the contents of -0 or 0 cells is always 0. This does not allow me to do a search a replace. I do not want to change these manually.
Can I force the + and - through using a psql command or can I use some other linux tool to change the -0 to N and 0 to P. I am using RHEL6.
Try using the decode function in place of the field name.
decode(myx_ecg_posneg,'-','N','+','P')
Update: Sorry, that's pl/sql. Try the case expression:
CASE myx_ecg_posneg
WHEN '-' THEN 'N'
WHEN '+' THEN 'P'
END