Revision Difference
binary_string#568354
<cat>Dev.Lua</cat>⤶
<title>Concepts - Binary Strings</title>⤶
⤶
# Binary Strings⤶
⤶
Lua has no separate type for binary data. A <page>string</page> is a sequence of bytes, any value from `0` to `255` can appear in it, including `0`, and the same type holds text and raw data.⤶
Which one you have depends on where the string came from and where it is going.⤶
⤶
Binary strings come out of <page>net.ReadData</page>, <page>util.Compress</page>, <page>util.Base64Decode</page>, <page>render.Capture</page>, <page>string.dump</page> and <page>File:Read</page>, and go into <page>net.WriteData</page>, <page>util.Decompress</page>, <page>File:Write</page> and <page>sound.Generate</page>.⤶
A BLOB column read with <page>sql.QueryTyped</page> is one as well.⤶
⤶
## Bytes vs. characters⤶
⤶
`#` and <page>string.len</page> count bytes. A character outside ASCII takes more than one, `#"\195\188"` is `2` for a single letter.⤶
Use <page>utf8.len</page> when you want characters, it also returns `false` when the string is not valid text.⤶
⤶
Strings cannot be changed in place. Build binary data with <page>string.char</page> and read it back with <page>string.byte</page>, both deal in byte values.⤶
Concatenation, <page>string.sub</page>, <page>string.rep</page> and <page>table.concat</page> keep every byte.⤶
For structured data, a <page text="File">file_class</page> opened in `rb` or `wb` mode has typed readers and writers such as <page>File:ReadULong</page> and <page>File:WriteFloat</page>. There is no `string.pack` in Garry's Mod.⤶
⤶
<note>`string.byte( str, 1, -1 )` returns one value per byte, and a long string errors with `string slice too long`. Read it in chunks.</note>⤶
⤶
## Where bytes get lost⤶
⤶
* <page>net.WriteString</page> writes a <page text="null terminated string">net.ReadString</page>, so it stops at the first `0` byte and the rest is never sent. Use <page>net.WriteData</page> for anything that might contain a `0`.⤶
* <page>net.WriteData</page> writes no length, so the receiver has to know how many bytes to read. Write the length first with <page>net.WriteUInt</page>, see <page text="Net Library Usage">Net_Library_Usage</page>. A compressed string that is the whole message can use the message length instead, as shown on <page>net.WriteData</page>.⤶
* <page>Global.print</page> and the console stop at the first `0` byte, so a binary string looks shorter than it is. Print `#str` or a hex dump instead.⤶
* <page>util.TableToJSON</page> keeps every byte and <page>util.JSONToTable</page> reads them all back. Bytes above `127` are written as they are, so the JSON is invalid UTF-8 and a browser or a web server will read it differently. Encode with <page>util.Base64Encode</page> first when the JSON leaves Lua or the data goes into a <page>DHTML</page> page. Pass `true` as the second argument, or the output gets a line break every 76 characters.⤶
* Patterns treat `.`, `%`, `(`, `[` and the other magic characters specially, and binary data is full of them. Search with <page>string.find</page> and `plain` set to `true`.⤶
⤶
<example>⤶
<description>⤶
A compressed string holds `0` bytes, so it goes over the net as data with its length in front.⤶
</description>⤶
<code>⤶
-- Server⤶
local packed = util.Compress( bigString )⤶
⤶
net.Start( "example_blob" )⤶
net.WriteUInt( #packed, 16 )⤶
net.WriteData( packed, #packed )⤶
net.Send( ply )⤶
⤶
-- Client⤶
net.Receive( "example_blob", function()⤶
local len = net.ReadUInt( 16 )⤶
local packed = net.ReadData( len )⤶
local bigString = util.Decompress( packed, 65536 )⤶
⤶
-- The first bytes as hex, to see what arrived⤶
print( ( packed:sub( 1, 8 ):gsub( ".", function( c ) return string.format( "%02X ", c:byte() ) end ) ) )⤶
end )⤶
</code>⤶
</example>
Garry's Mod
Rust
Steamworks
Wiki Help