Differences between revisions 2 and 9 (spanning 7 versions)
Revision 2 as of 2007-09-15 18:50:34
Size: 1946
Editor: irc2samus
Comment:
Revision 9 as of 2009-10-14 23:48:06
Size: 2397
Comment: Added a faster version of chr
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq71)]] <<Anchor(faq71)>>
Line 3: Line 3:

This task is quite easy while using the {{{printf}}} builtin. You can either write two simple functions as shown below or use the plain {{{printf}}} constructions alone.
If you have a known octal or hexadecimal value (at script-writing time), you can just use `printf`:
Line 7: Line 6:
   # POSIX
   printf '\x27\047\n'
}}}
This prints two literal ' characters (27 is the hexadecimal ASCII value of the character, and 47 is the octal value) and a newline.

If you need to convert characters (or numeric ASCII values) that are not known in advance (i.e., in variables), you can use something a little more complicated:
{{{
   # POSIX
Line 18: Line 25:
   # hex() - converts ASCII character to a hexadecimal value
   # unhex() - converts a hexadecimal value to an ASCII character
Line 20: Line 30:
   }

   unhex() {
      printf \\x"$1"
Line 28: Line 42:
The {{{ord}}} function above is quite tricky. It can be re-written in several other ways (use that one that will best suite your coding style or your actual needs). The {{{ord}}} function above is quite tricky.
Line 30: Line 44:
 ''Q: Tricky? Rather, it's using a feature that I can't find documented anywhere -- putting a single quote in front of an integer. Neat effect, but how on '''earth''' did you find out about it? Source diving? -- GreyCat''  ''Tricky? Rather, it's using a feature that I can't find documented anywhere -- putting a single quote in front of an integer. Neat effect, but how on '''earth''' did you find out about it? Source diving? -- GreyCat''
Line 32: Line 46:
 ''A: It validates The Single Unix Specification: "If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote." (see [http://www.opengroup.org/onlinepubs/009695399/utilities/printf.html printf()] to know more) -- mjf''   ''It validates The Single Unix Specification: "If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote." (see [[http://www.opengroup.org/onlinepubs/009695399/utilities/printf.html|printf()]] to know more) -- mjf''

This version of {{{chr}}} executes much faster than the {{{printf}}} version above (about 1/40 to less than 1/150 the time when run in a loop):
Line 35: Line 51:
   ord() {
     printf '%d' \"$1\"
   }
   chr() { echo -en "\0$(( $1 % 8 + 10 * ( $1 / 8 ) + 20 ))"; }
Line 40: Line 54:
Or: {{{
   for p in chr newchr; do time for i in {1..4000}; do $p 65 >/dev/null; done; done
Line 42: Line 57:
{{{
   ord() {
     printf '%d' \'$1\'
   }
   System1 System2
   real 0m46.824s real 1m33.814s
   user 0m4.624s user 0m8.540s
   sys 0m33.290s sys 1m23.978s

   real 0m1.340s real 0m0.512s
   user 0m1.096s user 0m0.389s
   sys 0m0.124s sys 0m0.096s
Line 47: Line 66:

Or, rather:

{{{
   ord() {
     printf '%d' "'$1'"
   }
}}}

Etc. All of the above {{{ord}}} functions should work properly. Which one you choose highly depends on particular situation.



There is also an alternative when printing characters by their ascii value that is using escape sequences like:
{{{
   echo $'\x27'
}}}
which prints a literal ' (there 27 is the hexadecimal ascii value of the character).
 

How do I convert an ASCII character to its decimal (or hexadecimal) value and back?

If you have a known octal or hexadecimal value (at script-writing time), you can just use printf:

   # POSIX
   printf '\x27\047\n'

This prints two literal ' characters (27 is the hexadecimal ASCII value of the character, and 47 is the octal value) and a newline.

If you need to convert characters (or numeric ASCII values) that are not known in advance (i.e., in variables), you can use something a little more complicated:

   # POSIX
   # chr() - converts decimal value to its ASCII character representation
   # ord() - converts ASCII character to its decimal value
 
   chr() {
     printf \\$(printf '%03o' $1)
   }
 
   ord() {
     printf '%d' "'$1"
   }

   # hex() - converts ASCII character to a hexadecimal value
   # unhex() - converts a hexadecimal value to an ASCII character

   hex() { 
      printf '%x' "'$1"
   }

   unhex() {
      printf \\x"$1"
   }

   # examples:
 
   chr $(ord A)    # -> A
   ord $(chr 65)   # -> 65

The ord function above is quite tricky.

  • Tricky? Rather, it's using a feature that I can't find documented anywhere -- putting a single quote in front of an integer. Neat effect, but how on earth did you find out about it? Source diving? -- GreyCat

    • It validates The Single Unix Specification: "If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote." (see printf() to know more) -- mjf

This version of chr executes much faster than the printf version above (about 1/40 to less than 1/150 the time when run in a loop):

   chr() { echo -en "\0$(( $1 % 8 + 10 * ( $1 / 8 ) + 20 ))"; }

   for p in chr newchr; do time for i in {1..4000}; do $p 65 >/dev/null; done; done

   System1                     System2
   real    0m46.824s           real    1m33.814s
   user    0m4.624s            user    0m8.540s
   sys     0m33.290s           sys     1m23.978s

   real    0m1.340s            real    0m0.512s
   user    0m1.096s            user    0m0.389s
   sys     0m0.124s            sys     0m0.096s

BashFAQ/071 (last edited 2021-02-08 16:03:51 by GreyCat)