Contents
Vala GIO Samples
GIO is similar to the Java IO framework. References to files and directories are represented by File objects which you can create from a file name or a URI. GIO provides two base classes for input and output: InputStream and OutputStream. Opening a file for reading its contents will result in a FileInputStream which is a sub-class of InputStream.
Streams can be wrapped according to the decorator pattern in order to provide them with additional functionality. For example it's possible to read the contents of a file line by line by decorating its FileInputStream with a DataInputStream. Or you could apply a FilterInputStream for custom filtering purposes.
Reading Text File Line by Line
The following example reads the contents of a text file line by line and prints them on the screen.
int main () {
// A reference to our file
var file = File.new_for_path ("data.txt");
if (!file.query_exists ()) {
stderr.printf ("File '%s' doesn't exist.\n", file.get_path ());
return 1;
}
try {
// Open file for reading and wrap returned FileInputStream into a
// DataInputStream, so we can read line by line
var dis = new DataInputStream (file.read ());
string line;
// Read lines until end of file (null) is reached
while ((line = dis.read_line (null)) != null) {
stdout.printf ("%s\n", line);
}
} catch (Error e) {
error ("%s", e.message);
}
return 0;
}
Compile and Run
$ valac --pkg gio-2.0 gio-sample.vala $ ./gio-sample
Note that you don't have to close streams explicitly. They are closed automatically as soon as they go out of scope (RAII).
File Objects
A File object is the representation of a path to a resource. For example, this can be a file but also a directory. It's important to note that this doesn't imply anything about the existence of the represented resource because File object itself does not do I/O operations but only stores the path. However, you can check for existence with the method query_exists ():
if (file.query_exists ()) {
// File or directory exists
}
In order to find out whether the resource is a directory or not you can query its FileType:
if (file.query_file_type (0) == FileType.DIRECTORY) {
// It's a directory
}
You can create File objects from a file system path or a URI:
var data_file = File.new_for_path ("data.txt");
var message_of_the_day = File.new_for_path ("/etc/motd");
var home_dir = File.new_for_path (Environment.get_home_dir ());
var web_page = File.new_for_uri ("http://live.gnome.org/Vala");
These methods are static factory methods, not constructors, since File is an interface. They create instances of classes implementing File.
GIO works transparently across various protocols. It doesn't matter if access is done directly on the local file system or via HTTP, FTP, SFTP, SMB or DAV. This is implemented by a virtual file system layer called GVFS which is designed to be extendable for backends of other protocols.
It will pay off to create file names and paths in your own application directly as File objects and to pass them as such to methods instead of representing them as plain strings. For example, it will be very easy to derive paths for parent and child directories without having to agonize over string manipulations and path separators:
var home_dir = File.new_for_path (Environment.get_home_dir ()); // ~
var bar_file = home_dir.get_child ("foo").get_child ("bar.txt"); // ~/foo/bar.txt
var foo_dir = bar_file.get_parent (); // ~/foo/
If you still need the name as a string you can get it either completely from the method get_path () or just the base name (meaning the last component) from get_basename ().
Some Simple File Operations
Creating, renaming, copying and deleting files. All operations in this sample are non-asynchronous.
int main () {
try {
// Reference a local file name
var file = File.new_for_path ("samplefile.txt");
{
// Create a new file with this name
var file_stream = file.create (FileCreateFlags.NONE);
// Test for the existence of file
if (file.query_exists ()) {
stdout.printf ("File successfully created.\n");
}
// Write text data to file
var data_stream = new DataOutputStream (file_stream);
data_stream.put_string ("Hello, world");
} // Streams closed at this point
// Determine the size of file as well as other attributes
var file_info = file.query_info ("*", FileQueryInfoFlags.NONE);
stdout.printf ("File size: %lld bytes\n", file_info.get_size ());
stdout.printf ("Content type: %s\n", file_info.get_content_type ());
// Make a copy of file
var destination = File.new_for_path ("samplefile.bak");
file.copy (destination, FileCopyFlags.NONE);
// Delete copy
destination.delete ();
// Rename file
var renamed = file.set_display_name ("samplefile.data");
// Move file to trash
renamed.trash ();
stdout.printf ("Everything cleaned up.\n");
} catch (Error e) {
stderr.printf ("Error: %s\n", e.message);
return 1;
}
return 0;
}
Note: On Windows files cannot be renamed while they are open. That's the reason for the extra block around the stream handling in this example. At the end of the block the stream resources will get freed as they get out of scope and the file will get closed.
Compile and Run
$ valac --pkg gio-2.0 gio-file-operations.vala $ ./gio-file-operations
Writing Data
This sample creates an output file, opens a stream to that file and writes some text to it. For possible long string writes a loop, which checks the number of bytes already written, is used.
int main () {
try {
// an output file in the current working directory
var file = File.new_for_path ("out.txt");
// delete if file already exists
if (file.query_exists ()) {
file.delete ();
}
// creating a file and a DataOutputStream to the file
/*
Use BufferedOutputStream to increase write speed:
var dos = new DataOutputStream (new BufferedOutputStream.sized (file.create (FileCreateFlags.REPLACE_DESTINATION), 65536));
*/
var dos = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));
// writing a short string to the stream
dos.put_string ("this is the first line\n");
string text = "this is the second line\n";
// For long string writes, a loop should be used, because sometimes not all data can be written in one run
// 'written' is used to check how much of the string has already been written
uint8[] data = text.data;
long written = 0;
while (written < data.length) {
// sum of the bytes of 'text' that already have been written to the stream
written += dos.write (data[written:data.length]);
}
} catch (Error e) {
stderr.printf ("%s\n", e.message);
return 1;
}
return 0;
}
Compile and Run
$ valac --pkg gio-2.0 gio-write-data.vala $ ./gio-write-data
Reading Binary Data
This sample reads header information of a BMP image file as well as the actual image data.
int main () {
try {
// Reference a BMP image file
var file = File.new_for_uri ("http://wvnvaxa.wvnet.edu/vmswww/images/test8.bmp");
// var file = File.new_for_path ("sample.bmp");
// Open file for reading
var file_stream = file.read ();
var data_stream = new DataInputStream (file_stream);
data_stream.set_byte_order (DataStreamByteOrder.LITTLE_ENDIAN);
// Read the signature
uint16 signature = data_stream.read_uint16 ();
if (signature != 0x4d42) { // this hex code means "BM"
stderr.printf ("Error: %s is not a valid BMP file\n", file.get_basename ());
return 1;
}
data_stream.skip (8); // skip uninteresting data fields
uint32 image_data_offset = data_stream.read_uint32 ();
data_stream.skip (4);
uint32 width = data_stream.read_uint32 ();
uint32 height = data_stream.read_uint32 ();
data_stream.skip (8);
uint32 image_data_size = data_stream.read_uint32 ();
// Seek and read the image data chunk
uint8[] buffer = new uint8[image_data_size];
file_stream.seek (image_data_offset, SeekType.CUR);
data_stream.read (buffer);
// Show information
stdout.printf ("Width: %ld px\n", width);
stdout.printf ("Height: %ld px\n", height);
stdout.printf ("Image data size: %ld bytes\n", image_data_size);
} catch (Error e) {
stderr.printf ("Error: %s\n", e.message);
return 1;
}
return 0;
}
Compile and Run
$ valac --pkg gio-2.0 gio-binary-sample.vala $ ./gio-binary-sample
Enumerating Directory Content
int main (string[] args) {
try {
var directory = File.new_for_path (".");
if (args.length > 1) {
directory = File.new_for_commandline_arg (args[1]);
}
var enumerator = directory.enumerate_children (FileAttribute.STANDARD_NAME, 0);
FileInfo file_info;
while ((file_info = enumerator.next_file ()) != null) {
stdout.printf ("%s\n", file_info.get_name ());
}
} catch (Error e) {
stderr.printf ("Error: %s\n", e.message);
return 1;
}
return 0;
}
Compile and Run
$ valac --pkg gio-2.0 gio-ls.vala $ ./gio-ls
Asynchronous File Listing
using Gtk;
/**
* Loads the list of files in user's home directory and displays them
* in a GTK+ list view.
*/
class ASyncGIOSample : Window {
private ListStore model;
public ASyncGIOSample () {
// Set up the window
set_default_size (300, 200);
this.destroy.connect (Gtk.main_quit);
// Set up the list widget and its model
this.model = new ListStore (1, typeof (string));
var list = new TreeView.with_model (this.model);
list.insert_column_with_attributes (-1, "Filename",
new CellRendererText (), "text", 0);
// Put list widget into a scrollable area and add it to the window
var scroll = new ScrolledWindow (null, null);
scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
scroll.add (list);
add (scroll);
// start file listing process
list_directory.begin ();
}
private async void list_directory () {
stdout.printf ("Start scanning home directory\n");
var dir = File.new_for_path (Environment.get_home_dir ());
try {
// asynchronous call, to get directory entries
var e = yield dir.enumerate_children_async (FileAttribute.STANDARD_NAME,
0, Priority.DEFAULT);
while (true) {
// asynchronous call, to get entries so far
var files = yield e.next_files_async (10, Priority.DEFAULT);
if (files == null) {
break;
}
// append the files found so far to the list
foreach (var info in files) {
TreeIter iter;
this.model.append (out iter);
this.model.set (iter, 0, info.get_name ());
}
}
} catch (Error err) {
stderr.printf ("Error: list_files failed: %s\n", err.message);
}
}
static int main (string[] args) {
Gtk.init (ref args);
var demo = new ASyncGIOSample ();
demo.show_all ();
Gtk.main ();
return 0;
}
}
Compile and Run
$ valac --pkg gtk+-3.0 gio-async.vala $ ./gio-async
Asynchronous Stream Reading
MainLoop main_loop;
async void read_something_async (File file) {
var text = new StringBuilder ();
print ("Start...\n");
try {
var dis = new DataInputStream (file.read ());
string line = null;
while ((line = yield dis.read_line_async (Priority.DEFAULT)) != null) {
text.append (line);
text.append_c ('\n');
}
print (text.str);
} catch (Error e) {
error (e.message);
}
main_loop.quit ();
}
void main (string[] args) {
var file = File.new_for_uri ("http://www.gnome.org");
if (args.length > 1) {
file = File.new_for_commandline_arg (args[1]);
}
main_loop = new MainLoop ();
read_something_async (file);
main_loop.run ();
}
Compile and Run
$ valac --pkg gio-2.0 gio-async-reading.vala $ ./gio-async-reading