# How to write data in binary to a file?

**URL:** <https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555>\
**Category:** ruby-talk\
**Created:** [10 September 2011 00:57 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555 "2011-09-10T00:57:27Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![frank\_hi](https://avatars.discourse-cdn.com/v4/letter/f/77aa72/32.png) [@frank\_hi](https://rubytalk.org/u/frank_hi)\
**Post date:** [10 September 2011 00:57 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/1 "2011-09-10T00:57:27Z")

</div>

Hi,

I want to write the data into a file in binary mode. The file is opened  
in binary mode by ofile=File.open("filename",'wb'). However, when I  
write the data using ofile.write(1), ruby will write the ascii code 31  
to the file. I want it to write one byte number withe value 1. How can I  
do it? I have been googling a lot and could not find the answer.

Thanks

Frank

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

---

<div class="post-metadata">

**Author:** ![Eric\_Wong2](https://avatars.discourse-cdn.com/v4/letter/e/b9e5f3/32.png) [@Eric\_Wong2](https://rubytalk.org/u/Eric_Wong2)\
**Post date:** [10 September 2011 01:32 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/2 "2011-09-10T01:32:59Z")

</div>

You need to pack the data into a string before writing. The following  
will write one unsigned byte:

&nbsp;&nbsp;buffer = [1].pack("C")  
&nbsp;&nbsp;file.write(buffer)

See `ri Array#pack` for full documentation on things you can do with  
pack (and String#unpack for the inverse).

There are also a nearly-identical `pack'/`unpack' functions in Perl.  
The "perlpacktut" manpage is a great read for this.

> **···**
>
> frank hi \<yw\_hi@163.com\> wrote:
> 
> > I want to write the data into a file in binary mode. The file is opened  
> > in binary mode by ofile=File.open("filename",'wb'). However, when I  
> > write the data using ofile.write(1), ruby will write the ascii code 31  
> > to the file. I want it to write one byte number withe value 1. How can I  
> > do it? I have been googling a lot and could not find the answer.
> 
> --  
> Eric Wong

---

<div class="post-metadata">

**Author:** ![7stud](https://avatars.discourse-cdn.com/v4/letter/7/57b2e6/32.png) [@7stud](https://rubytalk.org/u/7stud)\
**Post date:** [10 September 2011 03:48 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/3 "2011-09-10T03:48:24Z")

</div>

frank hi wrote in post #1021069:

> Hi,
> 
> I want to write the data into a file in binary mode. The file is opened  
> in binary mode by ofile=File.open("filename",'wb').

All that does is turn off newline conversions, so if you write \n to a  
file, \n will be written to the file no matter what operating system the  
program is running on.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Dominic\_Rose](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@Dominic\_Rose](https://rubytalk.org/u/Dominic_Rose)\
**Post date:** [10 September 2011 17:14 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/4 "2011-09-10T17:14:21Z")

</div>

Did you try with File#syswrite?

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

---

<div class="post-metadata">

**Author:** ![Brian\_Candler](https://avatars.discourse-cdn.com/v4/letter/b/5f9b8f/32.png) [@Brian\_Candler](https://rubytalk.org/u/Brian_Candler)\
**Post date:** [10 September 2011 16:52 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/5 "2011-09-10T16:52:35Z")

</div>

7stud -- wrote in post #1021082:

> > I want to write the data into a file in binary mode. The file is opened  
> > in binary mode by ofile=File.open("filename",'wb').
> 
> All that does is turn off newline conversions, so if you write \n to a  
> file, \n will be written to the file no matter what operating system the  
> program is running on. Well, and this:
> 
> # === ... And sets external encoding to ASCII-8BIT unless explicitly specified.

Oh no... yet another inconsistency from ruby 1.9.

The documentation you quote is correct in what it says:

irb(main):001:0\> f1 = File.open("/tmp/f1","w")  
=\> #\<File:/tmp/f1\>  
irb(main):002:0\> f2 = File.open("/tmp/f2","wb")  
=\> #\<File:/tmp/f2\>  
irb(main):003:0\> f1.external\_encoding  
=\> nil  
irb(main):004:0\> f2.external\_encoding  
=\> #\<Encoding:ASCII-8BIT\>

However, the behaviour this implies is unexpected:

irb(main):005:0\> f2.write("groß")  
=\> 5

Normally, transcoding a UTF-8 string (which contains non-ASCII  
characters) to ASCII-8BIT would raise an exception:

irb(main):006:0\> "groß".encode("ASCII-8BIT")  
Encoding::UndefinedConversionError: U+00DF from UTF-8 to ASCII-8BIT

But now we have a special case where it \*doesn't\* raise an exception ☹

Regards,

Brian.

(Tested with "ruby 1.9.2p0 (2010-08-18 revision 29036) [x86\_64-linux]")

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Phillip\_Gawlowski](https://avatars.discourse-cdn.com/v4/letter/p/ecc23a/32.png) [@Phillip\_Gawlowski](https://rubytalk.org/u/Phillip_Gawlowski)\
**Post date:** [10 September 2011 18:05 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/6 "2011-09-10T18:05:07Z")

</div>

You obviously aren't aware what Extended ASCII (aka ASCII 8bit) is.

Take a look at ISO 8859-1, and check what 11011111 encodes.

> **···**
>
> On Sat, Sep 10, 2011 at 6:52 PM, Brian Candler \<b.candler@pobox.com\> wrote:
> 
> > irb(main):005:0\> f2.write("groß")  
> > =\> 5
> > 
> > Normally, transcoding a UTF-8 string (which contains non-ASCII  
> > characters) to ASCII-8BIT would raise an exception:
> 
> --  
> Phillip Gawlowski
> 
> phgaw.posterous.com | twitter.com/phgaw | gplus.to/phgaw
> 
> A method of solution is perfect if we can forsee from the start,  
> and even prove, that following that method we shall attain our aim.  
> -- Leibniz

---

<div class="post-metadata">

**Author:** ![Brian\_Candler](https://avatars.discourse-cdn.com/v4/letter/b/5f9b8f/32.png) [@Brian\_Candler](https://rubytalk.org/u/Brian_Candler)\
**Post date:** [11 September 2011 03:15 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/7 "2011-09-11T03:15:20Z")

</div>

Phillip Gawlowski wrote in post #1021166:

> > irb(main):005:0\> f2.write("gro")  
> > =\> 5
> > 
> > Normally, transcoding a UTF-8 string (which contains non-ASCII  
> > characters) to ASCII-8BIT would raise an exception:
> 
> You obviously aren't aware what Extended ASCII (aka ASCII 8bit) is.

I'm not? Then please explain it to me. What I know so far about ruby 1.9  
encoding I have documented at

> <https://github.com/candlerb/string19/blob/master/string19.rb>

There are some 200+ behaviours there, reverse-engineered with tests.

However, I'm quite happy to have gaps in my knowledge filled out.

> Take a look at ISO 8859-1, and check what 11011111 encodes.

What has ISO-8859-1 got to do with this?

\*If\* the file had an external encoding of ISO-8859-1, then the character  
"ß" (two bytes in UTF-8) would have been translated to the single byte  
0xDF as it was written out.

But in this example, the external encoding is ASCII-8BIT, which is  
ruby's encoding for "ASCII characters in the low 128 values, and unknown  
binary in the high 128 values"

Ruby does not let you transcode UTF-8 to ASCII-8BIT, or back again, if  
there are any high-value characters in it. You can confirm this easily:

> > s1 = "groß"

=\> "groß"

> > s1.encode("ASCII-8BIT")

Encoding::UndefinedConversionError: U+00DF from UTF-8 to ASCII-8BIT  
&nbsp;&nbsp;from (irb):2:in `encode'  
&nbsp;&nbsp;from (irb):2  
&nbsp;&nbsp;from /usr/local/bin/irb192:12:in `\<main\>'

> > s2 = "gro\xDF".force\_encoding("ASCII-8BIT")

=\> "gro\xDF"

> > s2.encode("UTF-8")

Encoding::UndefinedConversionError: "\xDF" from ASCII-8BIT to UTF-8  
&nbsp;&nbsp;from (irb):4:in `encode'  
&nbsp;&nbsp;from (irb):4  
&nbsp;&nbsp;from /usr/local/bin/irb192:12:in `\<main\>'

Now, if you open a file for write and set an external encoding (the  
default is nil) it means "transcode to this encoding". But for some  
reason, setting external encoding to ASCII-8BIT bypasses this rule.

> **···**
>
> > On Sat, Sep 10, 2011 at 6:52 PM, Brian Candler \<b.candler@pobox.com\> \> wrote:
> 
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Robert\_K1](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/robert_k1/32/1830_2.png) [@Robert\_K1](https://rubytalk.org/u/Robert_K1)\
**Post date:** [12 September 2011 11:20 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/8 "2011-09-12T11:20:56Z")

</div>

> Phillip Gawlowski wrote in post #1021166:
> 
> > > irb(main):005:0\> f2.write("gro")  
> > > =\> 5
> > > 
> > > Normally, transcoding a UTF-8 string (which contains non-ASCII  
> > > characters) to ASCII-8BIT would raise an exception:
> > 
> > You obviously aren't aware what Extended ASCII (aka ASCII 8bit) is.
> 
> I'm not? Then please explain it to me. What I know so far about ruby 1.9  
> encoding I have documented at  
> [string19/string19.rb at master · candlerb/string19 · GitHub](https://github.com/candlerb/string19/blob/master/string19.rb)

irb(main):001:0\> u=Encoding::BINARY  
=\> #\<Encoding:ASCII-8BIT\>  
irb(main):002:0\> u.names  
=\> ["ASCII-8BIT", "BINARY"]

In other words: binary and ASCII-8BIT are the same.

I find the behavior totally consistent: when writing a String with  
\*any\* encoding to BINARY then simply bytes are dumped as is and \_no  
conversion\_ is done. So there cannot be an encoding error.

irb(main):003:0\> s="groß"  
=\> "groß"  
irb(main):004:0\> s.size  
=\> 4  
irb(main):005:0\> s.bytesize  
=\> 5  
irb(main):006:0\> s.encoding  
=\> #\<Encoding:UTF-8\>  
irb(main):007:0\> File.open("x","wb"){|io| p io.external\_encoding,  
io.internal\_encoding ; io.write(s)}  
#\<Encoding:ASCII-8BIT\>  
nil  
=\> 5  
irb(main):008:0\> File.stat("x").size  
=\> 5

Now I can read the file with encoding UTF-8 properly:

irb(main):009:0\> t = File.open("x","r:UTF-8") {|io| io.read}  
=\> "groß"  
irb(main):010:0\> t.size  
=\> 4  
irb(main):011:0\> t.bytesize  
=\> 5  
irb(main):012:0\> t.encoding  
=\> #\<Encoding:UTF-8\>  
irb(main):013:0\> s == t  
=\> true

> Now, if you open a file for write and set an external encoding (the  
> default is nil) it means "transcode to this encoding". But for some  
> reason, setting external encoding to ASCII-8BIT bypasses this rule.

See above. The name ASCII-8BIT is probably not the best one to choose  
for this but if you think about it as BINARY then everything fits  
nicely together.

Kind regards

robert

> **···**
>
> On Sun, Sep 11, 2011 at 5:15 AM, Brian Candler \<b.candler@pobox.com\> wrote:
> 
> > > On Sat, Sep 10, 2011 at 6:52 PM, Brian Candler \<b.candler@pobox.com\> \>\> wrote:
> 
> --  
> remember.guy do |as, often| as.you\_can - without end  
> [http://blog.rubybestpractices.com/](http://blog.rubybestpractices.com/)

---

<div class="post-metadata">

**Author:** ![Brian\_Candler](https://avatars.discourse-cdn.com/v4/letter/b/5f9b8f/32.png) [@Brian\_Candler](https://rubytalk.org/u/Brian_Candler)\
**Post date:** [12 September 2011 17:45 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/9 "2011-09-12T17:45:48Z")

</div>

Robert Klemme wrote in post #1021429:

> irb(main):002:0\> u.names  
> =\> ["ASCII-8BIT", "BINARY"]
> 
> In other words: binary and ASCII-8BIT are the same.

Indeed.

> I find the behavior totally consistent

Consistent with what? If you set external\_encoding to something other  
than nil, you are telling ruby to transcode strings on output to the  
given encoding - \*unless\* the given encoding is "ASCII-8BIT"/"BINARY".

So it seems that "ASCII-8BIT" is handled as a special case. In fact,  
looking at io.c, that encoding is handled as a special case all over the  
place:

$ egrep -n "= rb\_ascii8bit" io.c  
232:#define NEED\_WRITECONV(fptr) ((fptr-\>encs.enc != NULL &&  
fptr-\>encs.enc != rb\_ascii8bit\_encoding()) ||  
NEED\_NEWLINE\_DECORATOR\_ON\_WRITE(fptr) || (fptr-\>encs.ecflags &  
(ECONV\_DECORATOR\_MASK|ECONV\_STATEFUL\_DECORATOR\_MASK)))  
774: if (!fptr-\>encs.enc || (fptr-\>encs.enc ==  
rb\_ascii8bit\_encoding() && !fptr-\>encs.enc2)) {  
924: else if (fptr-\>encs.enc != rb\_ascii8bit\_encoding())  
3948: fptr-\>encs.enc = rb\_ascii8bit\_encoding();  
4203: if (intern == NULL && ext != rb\_ascii8bit\_encoding())

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Gary\_Wright](https://avatars.discourse-cdn.com/v4/letter/g/58956e/32.png) [@Gary\_Wright](https://rubytalk.org/u/Gary_Wright)\
**Post date:** [12 September 2011 18:54 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/10 "2011-09-12T18:54:13Z")

</div>

> > Now, if you open a file for write and set an external encoding (the  
> > default is nil) it means "transcode to this encoding". But for some  
> > reason, setting external encoding to ASCII-8BIT bypasses this rule.
> 
> See above. The name ASCII-8BIT is probably not the best one to choose  
> for this but if you think about it as BINARY then everything fits  
> nicely together.

I think the anomaly that Brian pointed out was that the transcoding during IO behaved differently than explicit transcoding:

> Normally, transcoding a UTF-8 string (which contains non-ASCII  
> characters) to ASCII-8BIT would raise an exception:
> 
> irb(main):006:0\> "groß".encode("ASCII-8BIT")  
> Encoding::UndefinedConversionError: U+00DF from UTF-8 to ASCII-8BIT
> 
> But now we have a special case where it \*doesn't\* raise an exception ☹

It makes sense to me that transcoding to 'binary' (or 'ASCII-8BIT') would just copy bytes but it doesn't make sense that implicit transcoding via IO and explicit transcoding via #encode behave differently.

Gary Wright

> **···**
>
> On Sep 12, 2011, at 7:20 AM, Robert Klemme wrote:

---

<div class="post-metadata">

**Author:** ![Robert\_K1](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/robert_k1/32/1830_2.png) [@Robert\_K1](https://rubytalk.org/u/Robert_K1)\
**Post date:** [12 September 2011 21:58 UTC](https://rubytalk.org/t/how-to-write-data-in-binary-to-a-file/63555/11 "2011-09-12T21:58:30Z")

</div>

The only special thing is that it is the "encoding" of a String's raw  
data, I'd say.

Cheers

robert

> **···**
>
> On Mon, Sep 12, 2011 at 7:45 PM, Brian Candler \<b.candler@pobox.com\> wrote:
> 
> > Robert Klemme wrote in post #1021429:
> > 
> > > irb(main):002:0\> u.names  
> > > =\> ["ASCII-8BIT", "BINARY"]
> > > 
> > > In other words: binary and ASCII-8BIT are the same.
> > 
> > Indeed.
> > 
> > > I find the behavior totally consistent
> > 
> > Consistent with what? If you set external\_encoding to something other  
> > than nil, you are telling ruby to transcode strings on output to the  
> > given encoding - \*unless\* the given encoding is "ASCII-8BIT"/"BINARY".
> > 
> > So it seems that "ASCII-8BIT" is handled as a special case. In fact,  
> > looking at io.c, that encoding is handled as a special case all over the  
> > place:
> 
> --  
> remember.guy do |as, often| as.you\_can - without end  
> [http://blog.rubybestpractices.com/](http://blog.rubybestpractices.com/)
