Replace alphabets in a string with their ASCII values

hi. I have a requirement where I need to REPLACE all alphabets from an alphanumeric input string into their respective ASCII decimal value. For example:

If the input string is ABAC123 , the output should be 65666567123

I am seeking a single line command, and I was trying searching for options of TR command.

Please help me.

thanks.
Kumarjit.

Hello kumarjt,

Could you please try following and let us know if this helps you.

echo "ABAC123" | od -A n -t d1

Thanks,
R. Singh

thanks for the lead Ravinder, but the output is not as expected.

I need 65666567123 , but with the command you gave, output is 65 66 65 67 49 50 51 10

thanks.
Kumarjit

[

Hi R. Singh,
Note that the output kumarjt wants to produce contains no spaces and only translates alphabetic characters (leaving numeric input characters unchanged) producing ambiguous output when numeric characters are included in the input .

With kumarjt's specified behavior, the output from both of the following input strings:
ABCA653 and ABCAA3 is the same string 65666765653 .

Hi kumarjt,
The tr utility translates one character to one character, it does not translate a single character to a pair of characters.

Hi,
with perl:

echo ABAC123 | perl -ne 'chomp;print map /[0-9]/ ? $_ : ord, split //'
65666567123

Regards.

Try

echo "ABAC123" | od -An -ctd1 | awk 'NR == 1 {split ($0, T); next} {for (i=1; i<=NF; i++) printf "%s", ($i>=65)?$i:($i<32)?"":T; printf RS}' 
65666567123

... may need some refinement ...

Hello kumarjt,

Could you please try following and let me know if this helps you.

awk -vs1="'" -vval="ABAC123" BEGIN'{num=split(val,array,"");for(i=1;i<=num;i++){if(array ~ /[[:alpha:]]/){Q=Q?Q array:array} else {printf("%d",array) >> "temp_file2"}};system("printf " Q "| od -A n -t d1 > temp_file;awk " s1 "{gsub(/ /,X,$0);V=V?V $0:$0} END{print V}" s1 " temp_file temp_file2;rm temp_file temp_file2");}'

Output will be as follows.

65666567123

So above command will look for only alphabets and convert them into their ASCII their values with printing digits values as it is.
EDIT: Adding a non-one liner form of above solution too now.

 awk -vs1="'" -vval="ABAC123" BEGIN'{
                                        num=split(val,array,"");
                                        for(i=1;i<=num;i++){
                                                                if(array ~ /[[:alpha:]]/){
                                                                                                Q=Q?Q array:array
                                                                                            }
                                                                else                        {
                                                                                                printf("%d",array) >> "temp_file2"
                                                                                            }
                                                           };
                                        system("printf " Q "| od -A n -t d1 > temp_file;awk " s1 "{gsub(/ /,X,$0);V=V?V $0:$0} END{print V}" s1 " temp_file temp_file2;rm temp_file temp_file2");
                                   }
                                  '
 

Thanks,
R. Singh

@RavinderSingh13: What happens if the to-be-converted string has trailing alpha chars, e.g. val="AB123DE" ?

1 Like

Of all the replies, Don Cragun's has been the eye opener for me.

I thank all of you for the support, but the actuality is ABCA653 and ABCAA3
needs to be handled seperately even if the expected command evaluates to the same numerical value, which is 65666567123 .

Let me brief you my business requirement, which I can manifest only to the slightest amount.
these alphanumeric strings are portfolio#'s, and we are receiving multiple files for each portfolio#, and we are required to always move files for each folio into the same folder.

We have to distribute all folio files in a round robin manner into 3 folders and all files for ABCA653 should be moved into a single folder, and similarly for ABCAA3 , but these two are totally different folio#.

How can I do that?

Please revert back if you need any further info.

thanks.
Kumarjit

Ok,
Can you change your specifies as below ?

  • letter -> hex value
  • number (0-9) -> 00-09
  • In this case, "ABCA653" -> "65666765060503"
  • and "ABCAA3" -> "656667656503"

In perl:

echo "ABCA653" | perl -ne 'chomp;print map /[0-9]/ ? "0".$_ : ord, split //'

Regards.

Hello RudiC,

Thank you for letting me know here. So following are the 2 solutions.
I- Which will work for a variable as per OP's request.
II- Where we could pass an Input_file to it.
Solution 1st:

awk -vs1="'" -vval="AB123DE" BEGIN'{
                                        num=split(val,array,"");
                                        for(i=1;i<=num;i++){
                                                                if(array ~ /[[:alpha:]]/){
                                                                                                system("printf " array "| od -A n -t d1 >> temp_file")
                                                                                            }
                                                                else                        {
                                                                                                printf("%d",array) >> "temp_file"
                                                                                            }
                                                           };
                                        system("awk " s1 "{sub(/^ +/,X,$0);W=W?W $0:$0} END{sub(/ +/,X,W);print W;} " s1 " temp_file;rm temp_file");
                                   }'
 

Output will be as follows.

65661236869

Solution 2nd:
Let's say we have following Input_file:

cat  Input_file
ASBVC123
1234ERDTSFGJ
ABSSFDG1231ASASSD
1221ASAWWRT1221

Then following is the solution for above Input_file.

awk -vs2="\"" -vs1="'"                    '{
                                        num=split($0,array,"");
                                        for(i=1;i<=num;i++){
                                                                if(array ~ /[[:alpha:]]/){
                                                                                                system("printf " array "| od -A n -t d1 >> tempfile")
                                                                                            }
                                                                if(array !~ /[[:alpha:]]/){
                                                                                                system("printf " array " >> tempfile")
                                                                                            }
                                                           };
                                        system("awk " s1 "{sub(/^ +/,X,$0);W=W?W $0:$0} END{sub(/ +/,X,W);print W;W=" s2 s2 "} " s1 " tempfile;rm tempfile");
                                           }
                                          '      Input_file

Output will be as follows.

6583668667123
12346982688483707174
656683837068711231658365838368
1221658365878782841221

Thanks,
R. Singh

Thanks for the help disedorgue. Just the thing I wanted.....

But, can this one liner be acheived by any other command than PERL?
The reason for asking so is in our project, we've not used/abstained from using PERL so far.

Any help is much appreciated.

thanks.
Kumarjit

Change the %s in post#6 to %02d .

If you have an aversion to perl, and one-liner is a must, try sed -f command_file and put

s/A/65/
s/B/66/
etc

into the command_file.

Juha :slight_smile:

Just with awk:

 echo ABCA341 | awk '{
  for(i=0;i<=255;i++){A[sprintf("%c", i + 0)]=i;}
}
{
  split($1,B,"")
  for(i=1;i<=length(B);++i){
     if(B<=9){
       printf("%02d",B)
     }
     else{
       printf("%i",A[b])
     }
  }
}'
65666765030401

Regards.

[LEFT]Hi,
Perl suits perfectly for the work : the ord function converts a character to its ascii decimal value.
See:

$ echo -n This works fine  |perl  -ne 'for ($i=0; $i < length ; $i++) { print   ord (substr $_,$i,1) ," " } '
84 104 105 115 32 119 111 114 107 115 32 102 105 110 101

[/LEFT]

Hi,
@blastit.fr : perl solution already proposed but it isn't possible to use (read post#12).

Otherwise, another way in pure builtins bash:

$ X=ABCD234;i=0;while ((i<${#X})); do [[ "${X:$i:1}" =~ [0-9] ]] || Y=\';printf "%02d" "${Y}${X:((i++)):1}";Y=""; done
65666768020304

or

echo -n ABCDE123 | while read -n 1 -d "" X ; do [[ $X =~ [0-9] ]] || Y=\';printf "%02d" "${Y}${X}";Y=""; done
6566676869010203

Regards.

1 Like

Brilliant. Where can I learn more about this trick printing "'A" ?

Hi RudiC,
This trick (not just in bash) validate (in : printf ) :

Regards.

1 Like

Thanks for the reference. Even though it works as specified, it is not mentioned in the respective linux man page (although it is in FreeBSD).