10 - Arquivos

10.1 Introdução

Uma definição clássica de arquivos — files é que são estruturas organizadas, armazenadas e manipuladas em um dispositivo de memória secundária (por ex., hd, pen-drives, disquetes), sendo lidos ou escritos por um programa.

Em C++, o conceito de arquivos é ampliado para incluir dispositivos de entrada (por ex., teclado, portas de comunicação) e saída (por ex., vídeo, impressora). Sendo assim, C++ trata arquivos como sendo um stream — fluxo de caracteres. Os comandos cout e cin são exemplos de streams para um dispositivo de saída (stdout) e entrada (stdin), respectivamente. Quando é utilizado um comando como: cout << 10; o valor 10 é enviado como uma stream para o stdout, neste caso, o vídeo ou monitor.

Para manipular arquivos como streams é necessária a inclusão de uma biblioteca fstream nos programas em C++. No trecho de código à seguir é descrito um programa para associar uma variável fout do tipo ofstream com um arquivo arquivo.txt. Caso o arquivo arquivo.txt possa ser aberto para escrita, a variável fout aceita o uso dos operadores <<, assim como o cout.

#include<fstream>
#include<iostream>
using namespace std;
 
int main()
{
    // Cria uma variável para controlar o fluxo de saída
    ofstream fout;
    // Associa um arquivo com o fluxo de saída
    fout.open("arquivo.txt");
    // Verifica se o arquivo pode ser criado
    if (fout.is_open())
    {
        // direciona os caracteres para o fluxo de saída
        fout << "Ser ou nao ser...\n"
            << "Eis a questao...\n\n"
            << "William Shakeaspare\n";
        // fecha o fluxo de saída
        fout.close();
    }
    // caso haja algum erro para abrir para escrita
    else {
        cout << "arquivo.txt nao pode ser aberto.\n";
    }
 
    return 0;
}

O trecho seguinte de código é referente a leitura do arquivo arquivo.txt produzido no programa anterior. Neste caso, fin é do tipo ifstream que associa um arquivo a ser aberto para leitura.

#include<fstream>
#include<iostream>
using namespace std;
 
int main()
{
    // Cria uma variável para controlar o fluxo de entrada
    ifstream fin;
    // Cria uma variável para receber cada linha do arquivo
    char buff[255];
    // Associa um arquivo com o fluxo de entrada
    fin.open("arquivo.txt");
    // Verifica se o arquivo pode ser aberto para leitura
    if (fin.is_open())
    {
        // enquanto não atingir o final do arquivo
        while(!fin.eof())
        {
            // leia uma linha do arquivo, no máximo 255 caracteres
            fin.getline(buff, 255);
            // imprime cada linha do arquivo
            cout << buff << "\n";
        }
        // fecha o fluxo de entrada
        fin.close();
    }
    // caso haja algum erro para abrir para leitura
    else {
        cout << "arquivo.txt nao pode ser aberto.\n";
    }
 
    return 0;
}

Para manipular arquivos em C++, basicamente, é estipular o tipo do fluxo, definir uma variável qualquer que irá manipular esse fluxo e associar um arquivo com a variável, por meio de uma função open.

Tipo de Fluxo Descrição
ifstream Indica que é um fluxo de entrada — input filestream
ofstream Indica que é um fluxo de saída — output filestream

<Tipo de Fluxo> variavel;

variavel.open("nome_de_um_arquivo_qualquer");

10.2 Arquivos textuais

Um arquivo textual consiste de caracteres alfanuméricos, sinais de pontuação e de símbolos existentes no teclado. Arquivo com extensões de arquivos txt e html são exemplos de arquivos textuais.

Nas próximas subseções serão mostrados exemplos de manipulação de arquivos textuais para:

  • gravação de caracteres em um arquivo;
  • leitura de caracteres de um arquivo;
  • gravação de dados de uma estrutura em um arquivo;
  • leitura de dados de uma estrutura de um arquivo.

10.2.1 Gravação de caracteres em um arquivo

O programa descrito a seguir realiza as operações:

  • um nome de arquivo é pedido para ser inserido;
  • verifica se o arquivo pode ser aberto para escrita;
  • caso possa, o usuário pode digitar quaisquer caracteres que serão inseridos no arquivo de saída. Caso o usuário digite '.' (ponto) a leitura é terminada e o arquivo é fechado.

Observação: caso a função close não seja chamada para fechar o arquivo ao término do programa, o próprio programa em C++ realiza esta tarefa.

#include<cstdio>
#include<fstream>
#include<iostream>
using namespace std;
 
int main()
{
    /* Variáveis locais */
    char nomeArquivo[81];   // nome do arquivo
    char ch = 0;
 
    // Espera o usuário digitar um nome de arquivo válido
    cout << "Nome do arquivo: ";
    gets(nomeArquivo);
 
    // ofs é uma variável para fluxo de saída
    ofstream ofs;
    // ofs tenta abrir um arquivo
    ofs.open(nomeArquivo);
    // se conseguir abrir o arquivo para o fluxo de saída
    if(ofs.is_open())
    {
        // enquanto ch for diferente de .
        while(ch != '.')
        {
            // lê um caracter do teclado e atribui para ch
            ch = cin.get();
            // coloca o caracter no fluxo de saída
            ofs.put(ch);
        }
        // fecha o arquivo de saída
        ofs.close();
    }
    // caso não consiga abrir o arquivo
    else
    {
        cout << "ERRO: O arquivo " << nomeArquivo << " nao pode ser aberto.\n";
    }
    return 0;
}

10.2.2 Leitura de caracteres de um arquivo

O programa seguinte lê os caracteres de um arquivo qualquer e mostra na tela. Ao término do programa são mostrados o número de linhas do arquivo e a quantidade de caracteres do arquivo.

#include<cstdio>
#include<fstream>
#include<iostream>
using namespace std;
 
int main()
{
    /* Variáveis locais */
    char nomeArquivo[81];   // nome do arquivo
    char caractere;         // 1 caractere do arquivo
    int contaCaracteres;    // # caracteres do arquivo
    int contaLinhas;        // # linhas do arquivo
 
    // Espera o usuário digitar um nome de arquivo válido
    cout << "Nome do arquivo: ";
    gets(nomeArquivo);
 
    // Cria uma variável para referenciar o arquivo a ser
    // aberto para leitura
    ifstream ifs;
    // Abre o arquivo para leitura
    ifs.open(nomeArquivo);
    // checa se o arquivo existe para ser aberto
    if (ifs.is_open())
    {
        // zera os contadores
        contaCaracteres = 0;
        contaLinhas = 1;
        // enquanto não atingir o final do arquivo
        while(ifs.good())
        {
            // "pega" um caracter do arquivo
            caractere = (char) ifs.get();
            // mostra o caracter "pego" do arquivo
            cout << caractere;
            // se o caractere for uma quebra de linha '\n'
            if (caractere == '\n')
                contaLinhas++;
            // incrementa o contador de caracteres
            // indiferente se for uma quebra de linha
            contaCaracteres++;
        }
        // mostra os valores dos contadores
        cout << endl;
        cout << "# linhas     = " << contaLinhas << endl;
        cout << "# caracteres = " << contaCaracteres << endl;
        // fecha o arquivo
        ifs.close();
    }
    else {
        cout << "ERRO: O arquivo " << nomeArquivo << " nao pode ser aberto.\n";
    }
    return 0;
}

10.2.3 Gravação de dados de uma estrutura em um arquivo

O programa seguinte grava os dados de uma variável de um tipo registro definido como Funcionario em um arquivo-texto. Cada um dos campos do registro são escritos em uma linha separadamente. Note que as funções que aceitam uma variável do tipo ofstream deve ser um parâmetro por referência, visto que são feitas alterações no fluxo de saída.

#include <cstdio>
#include <fstream>
#include <iostream>
using namespace std;
//
struct Funcionario
{
    int codigo;
    char nome[81];
    float salario;
    char sexo;
};
// leNomeArquivo: o usuário deve digitar o nome de um arquivo para ser aberto
void leNomeArquivo(char nomeArquivo[])
{
    cout << "Nome do arquivo: ";
    fflush(stdin);
    gets(nomeArquivo);
}
// leFuncionario: o usuário deve digitar as informações para as variáveis-membro da estrutura Funcionario
void leFuncionario(Funcionario &f)
{
    cout << "Leitura de dados de um funcionario\n\n";
    cout << "Codigo.: ";
    cin >> f.codigo;
    cout << "Nome...: ";
    fflush(stdin);
    gets(f.nome);
    cout << "Salario: ";
    cin >> f.salario;
    cout << "Sexo...: ";
    cin >> f.sexo;
}
// abrirArquivo: a função retorna true se o arquivo pode ser aberto
bool abrirArquivo(ofstream &ofs, char nomeArquivo[])
{
    ofs.open(nomeArquivo);
    return ofs.is_open();
}
// gravaFuncionario: a função grava as informações da estrutura Funcionario no arquivo
void gravaFuncionario(ofstream &ofs, Funcionario &f)
{
    ofs << f.codigo << endl;
    ofs << f.nome << endl;
    ofs << f.salario << endl;
    ofs << f.sexo << endl;
}
int main()
{
    // nome do arquivo a ser aberto para escrita
    char nomeArquivo[81];
    //
    Funcionario funcionario;
    //
    ofstream ofs;
 
    // lê os dados de um funcionário
    leFuncionario(funcionario);
    // lê o nome do arquivo a ser aberto para escrito
    leNomeArquivo(nomeArquivo);
    // se o arquivo existe e pode ser aberto
    if(abrirArquivo(ofs, nomeArquivo) == true)
    {
        // grava informações no arquivo
        gravaFuncionario(ofs, funcionario);
    }
    // se o arquivo não pode ser aberto
    else
    {
        cout << "Arquivo: " << nomeArquivo
             << " nao pode ser aberto para escrita.\n";
    }
    return 0;
}

O programa seguinte grava as informações de um vetor do tipo registro Funcionario em um arquivo-texto. Observe que cada um dos campos são escritos em uma linha separadamente e todo o conteúdo do vetor funcionarios é escrito no arquivo.

#include <cstdio>
#include <fstream>
#include <iostream>
using namespace std;
//
const int MAX_FUNCS = 2;
//
struct Funcionario
{
    int codigo;
    char nome[81];
    float salario;
    char sexo;
};
// leNomeArquivo: o usuário deve digitar o nome de um arquivo para ser aberto
void leNomeArquivo(char nomeArquivo[])
{
    cout << "Nome do arquivo: ";
    fflush(stdin);
    gets(nomeArquivo);
}
// leFuncionarios: o usuário deve digitar as informações para as variáveis-membro do vetor de estrutura Funcionario
void leFuncionarios(Funcionario f[MAX_FUNCS])
{
    for (int i = 0; i < MAX_FUNCS; i++)
    {
        cout << "Leitura de dados de um funcionario\n\n";
        cout << "Codigo.: ";
        cin >> f[i].codigo;
        cout << "Nome...: ";
        fflush(stdin);
        gets(f[i].nome);
        cout << "Salario: ";
        cin >> f[i].salario;
        cout << "Sexo...: ";
        cin >> f[i].sexo;
    }
}
// abrirArquivo: a função retorna true se o arquivo pode ser aberto
bool abrirArquivo(ofstream &ofs, char nomeArquivo[])
{
    ofs.open(nomeArquivo);
    return ofs.is_open();
}
// gravaFuncionarios: a função grava as informações do vetor de estrutura Funcionario no arquivo
void gravaFuncionarios(ofstream &ofs, Funcionario f[MAX_FUNCS])
{
    for (int i = 0; i < MAX_FUNCS; i++)
    {
        ofs << f[i].codigo << endl;
        ofs << f[i].nome << endl;
        ofs << f[i].salario << endl;
        ofs << f[i].sexo << endl;
    }
}
int main()
{
    // nome do arquivo a ser aberto para escrita
    char nomeArquivo[81];
    //
    Funcionario funcionarios[MAX_FUNCS];
    //
    ofstream ofs;
 
    // lê os dados do vetor de funcionários
    leFuncionarios(funcionarios);
    // lê o nome do arquivo a ser aberto para escrita
    leNomeArquivo(nomeArquivo);
    // se o arquivo existe e pode ser aberto
    if(abrirArquivo(ofs, nomeArquivo) == true)
    {
        // grava informações no arquivo
        gravaFuncionarios(ofs, funcionarios);
    }
    // se o arquivo não pode ser aberto
    else
    {
        cout << "Arquivo: " << nomeArquivo
             << " nao pode ser aberto para escrita.\n";
    }
    return 0;
}

10.2.4 Leitura de dados de uma estrutura de um arquivo

O seguinte trecho de código C++ mostra os dados gravados de um tipo registro Funcionario de um arquivo-texto executado pelo primeiro programa da subseção anterior.

#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
using namespace std;
//
struct Funcionario
{
    int codigo;
    char nome[81];
    float salario;
    char sexo;
};
// leNomeArquivo: o usuário deve digitar o nome de um arquivo para ser aberto
void leNomeArquivo(char nomeArquivo[])
{
    cout << "Nome do arquivo: ";
    fflush(stdin);
    gets(nomeArquivo);
}
// mostraFuncionario: são impressas as informações para as variáveis-membro da estrutura Funcionario
void mostraFuncionario(Funcionario f)
{
    cout << "Leitura de dados de um funcionario\n\n";
    cout << "Codigo.: " << f.codigo << endl;
    cout << "Nome...: " << f.nome << endl;
    cout << "Salario: " << f.salario << endl;
    cout << "Sexo...: " << f.sexo << endl;
}
// abrirArquivo: a função retorna true se o arquivo pode ser aberto
bool abrirArquivo(ifstream &ifs, char nomeArquivo[])
{
    ifs.open(nomeArquivo);
    return ifs.is_open();
}
// leFuncionario: a função lê as informações da estrutura Funcionario do arquivo
void leFuncionario(ifstream &ifs, Funcionario &f)
{
    // lê cada um dos campos, linha a linha
    ifs >> f.codigo;
    ifs >> f.nome;
    ifs >> f.salario;
    ifs >> f.sexo;
}
int main()
{
    // nome do arquivo a ser aberto para leitura
    char nomeArquivo[81];
    //
    Funcionario funcionario;
    //
    ifstream ifs;
 
    // lê o nome do arquivo a ser aberto para leitura
    leNomeArquivo(nomeArquivo);
    // se o arquivo existe e pode ser aberto
    if(abrirArquivo(ifs, nomeArquivo) == true)
    {
        // lê as informações do arquivo
        leFuncionario(ifs, funcionario);
        // mostra os dados de um funcionário
        mostraFuncionario(funcionario);
    }
    // se o arquivo não pode ser aberto
    else
    {
        cout << "Arquivo: " << nomeArquivo
             << " nao pode ser aberto para leitura.\n";
    }
    return 0;
}

O trecho seguinte de código C++ lê os dados gravados de um arquivo-texto para um vetor de Funcionario. Este é o programa para ler os dados gravados do segundo programa da subseção anterior.

#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
using namespace std;
//
const int MAX_FUNCS = 2;
//
struct Funcionario
{
    int codigo;
    char nome[81];
    float salario;
    char sexo;
};
// leNomeArquivo: o usuário deve digitar o nome de um arquivo para ser aberto
void leNomeArquivo(char nomeArquivo[])
{
    cout << "Nome do arquivo: ";
    fflush(stdin);
    gets(nomeArquivo);
}
// mostraFuncionarios: são impressas as informações das variáveis-membro da estrutura Funcionario
void mostraFuncionarios(Funcionario f[MAX_FUNCS])
{
    for (int i = 0; i < MAX_FUNCS; i++)
    {
        cout << "Leitura de dados de um funcionario\n\n";
        cout << "Codigo.: " << f[i].codigo << endl;
        cout << "Nome...: " << f[i].nome << endl;
        cout << "Salario: " << f[i].salario << endl;
        cout << "Sexo...: " << f[i].sexo << endl;
    }
}
// abrirArquivo: a função retorna true se o arquivo pode ser aberto
bool abrirArquivo(ifstream &ifs, char nomeArquivo[])
{
    ifs.open(nomeArquivo);
    return ifs.is_open();
}
// leFuncionarios: a função lê as informações da estrutura Funcionario do arquivo
void leFuncionarios(ifstream &ifs, Funcionario f[MAX_FUNCS])
{
    char buff[81];
    for (int i = 0; i < MAX_FUNCS; i++)
    {
        ifs >> f[i].codigo;
        ifs >> f[i].nome;
        ifs >> f[i].salario;
        ifs >> f[i].sexo;
    }
}
int main()
{
    // nome do arquivo a ser aberto para leitura
    char nomeArquivo[81];
    //
    Funcionario funcionarios[MAX_FUNCS];
    //
    ifstream ifs;
 
    // lê o nome do arquivo a ser aberto para leitura
    leNomeArquivo(nomeArquivo);
    // se o arquivo existe e pode ser aberto
    if(abrirArquivo(ifs, nomeArquivo) == true)
    {
        // lê as informações do arquivo
        leFuncionarios(ifs, funcionarios);
        // mostra os dados de um funcionário
        mostraFuncionarios(funcionarios);
    }
    // se o arquivo não pode ser aberto
    else
    {
        cout << "Arquivo: " << nomeArquivo
             << " nao pode ser aberto para leitura.\n";
    }
    return 0;
}

10.3 Arquivos binários

Arquivos binários, tipicamente, são formados por sequências de bytes ou agrupamentos ordenados de 8 bits. Quando cria-se um formato de arquivo para um programa, o programador arranja os bytes em um formato que armazena as informações necessárias para a aplicação. Formatos de arquivos binários incluem múltiplos tipos de dados no mesmo arquivo, tais como: dados de imagem, vídeo e áudio (http://www.fileinfo.com/help/binary-vs-text-files.html). Um arquivo binário, comumente, possui um cabeçalho (header) no qual constam informações essenciais para que os programas possam ler os dados gravados após o cabeçalho denominado body.

Para manipular arquivos-binários em C/C++ é necessário estipular o tipo do fluxo, definir uma variável qualquer que irá manipular esse fluxo e associar um arquivo com a variável, por meio de uma função open. Por fim, após o nome do arquivo a ser aberto, coloca-se ios::binary.

<Tipo de Fluxo> variavel;

variavel.open("nome_de_um_arquivo_qualquer", ios::binary);

10.3.1 Escrita de dados de um vetor de Funcionários em um arquivo-binário

O programa a seguir lê as informações de um ou mais funcionários dependendo da quantidade que pode variar de 1 (um) até no máximo MAX_FUNCS. Cada um dos campos do tipo Funcionario são gravados seqüencialmente. O cabeçalho deste arquivo terá a quantidade de funcionários gravados.

#include <fstream>
#include <iostream>
using namespace std;
//
const int MAX_FUNCS = 3;
//
struct Funcionario
{
    int codigo;
    char nome[81];
    float salario;
    char sexo;
};
// leNomeArquivo: o usuário deve digitar o nome de um arquivo para ser aberto
void leNomeArquivo(char nomeArquivo[])
{
    cout << "Nome do arquivo: ";
    fflush(stdin);
    gets(nomeArquivo);
}
// leFuncionarios: o usuário deve digitar as informações para as variáveis-membro da estrutura Funcionario
void leFuncionarios(Funcionario f[], int quantidade)
{
    for (int i = 0; i < quantidade; i++)
    {
        cout << "Leitura de dados de um funcionario\n\n";
        cout << "Codigo.: ";
        cin >> f[i].codigo;
        cout << "Nome...: ";
        fflush(stdin);
        gets(f[i].nome);
        cout << "Salario: ";
        cin >> f[i].salario;
        cout << "Sexo...: ";
        cin >> f[i].sexo;
    }
}
// abrirArquivo: a função retorna true se o arquivo pode ser aberto
bool abrirArquivo(ofstream &ofs, char nomeArquivo[])
{
    ofs.open(nomeArquivo, ios::binary);
    return ofs.is_open();
}
// gravaFuncionarios: a função grava as informações da estrutura Funcionario no arquivo
void gravaFuncionarios(ofstream &ofs, Funcionario f[], int quantidade)
{
    // grava a quantidade de funcionarios
    ofs.write((char*)&quantidade, sizeof(int));
    // grava cada um dos funcionários como um vetor de caracteres
    // a transformação é feita na função ofs.write()
    for (int i = 0; i < quantidade; i++)
        ofs.write((char*)&f[i], sizeof(f[i]));
}
int main()
{
    // nome do arquivo a ser aberto para escrita
    char nomeArquivo[81];
    //
    Funcionario funcionarios[MAX_FUNCS];
    //
    ofstream ofs;
 
    // lê os dados de funcionários
    leFuncionarios(funcionarios, MAX_FUNCS);
    // lê o nome do arquivo a ser aberto para escrita
    leNomeArquivo(nomeArquivo);
    // se o arquivo existe e pode ser aberto
    if(abrirArquivo(ofs, nomeArquivo) == true)
    {
        // grava informações no arquivo
        gravaFuncionarios(ofs, funcionarios, MAX_FUNCS);
        // fecha o arquivo de saída
        ofs.close();
    }
    // se o arquivo não pode ser aberto
    else
    {
        cout << "Arquivo: " << nomeArquivo
             << " nao pode ser aberto para escrita.\n";
    }
    return 0;
}

10.3.2 Leitura dos dados de um arquivo-binário para um vetor de Funcionários

O programa seguinte lê o arquivo binário criado pelo programa da subseção anterior, tendo-se o cuidado de ler o cabeçalho e depois os dados gravados para um vetor do tipo Funcionario.

#include <fstream>
#include <iostream>
using namespace std;
//
const int MAX_FUNCS = 3;
//
struct Funcionario
{
    int codigo;
    char nome[81];
    float salario;
    char sexo;
};
// leNomeArquivo: o usuário deve digitar o nome de um arquivo para ser aberto
void leNomeArquivo(char nomeArquivo[])
{
    cout << "Nome do arquivo: ";
    fflush(stdin);
    gets(nomeArquivo);
}
// mostraFuncionarios: são impressas as informações para as variáveis-membro da estrutura Funcionario
void mostraFuncionarios(Funcionario f[], int quantidade)
{
    for (int i = 0; i < quantidade; i++)
    {
        cout << "Leitura de dados de um funcionario\n\n";
        cout << "Codigo.: " << f[i].codigo << endl;
        cout << "Nome...: " << f[i].nome << endl;
        cout << "Salario: " << f[i].salario << endl;
        cout << "Sexo...: " << f[i].sexo << endl;
    }
}
// abrirArquivo: a função retorna true se o arquivo pode ser aberto
bool abrirArquivo(ifstream &ifs, char nomeArquivo[])
{
    ifs.open(nomeArquivo, ios::binary);
    return ifs.is_open();
}
// leFuncionarios: a função lê as informações do vetor da estrutura Funcionario do arquivo
void leFuncionarios(ifstream &ifs, Funcionario f[], int &quantidade)
{
    // lê o número de funcionários gravados no cabeçalho
    ifs.read((char*)&quantidade, sizeof(int));
    // lê os dados do arquivo para um vetor de funcionários
    for (int i = 0; i < quantidade; i++)
        ifs.read((char*)&f[i], sizeof(f[i]));
}
int main()
{
    // nome do arquivo a ser aberto para leitura
    char nomeArquivo[81];
    //
    Funcionario funcionarios[MAX_FUNCS];
    //
    ifstream ifs;
 
    // lê o nome do arquivo a ser aberto para leitura
    leNomeArquivo(nomeArquivo);
    // se o arquivo existe e pode ser aberto
    if(abrirArquivo(ifs, nomeArquivo) == true)
    {
        int quantidade = 0;
        // lê as informações do arquivo
        leFuncionarios(ifs, funcionarios, quantidade);
        // mostra os dados dos funcionários
        mostraFuncionarios(funcionarios, quantidade);
        //
        ifs.close();
    }
    // se o arquivo não pode ser aberto
    else
    {
        cout << "Arquivo: " << nomeArquivo
             << " nao pode ser aberto para leitura.\n";
    }
    return 0;
}

10.4 Exercícios

  1. Refaça os programas da Seção 10.1, criando funções para cada ação realizada no arquivo.
  2. Refaça o programa da Subeção 10.2.1, criando funções para cada ação realizada no arquivo.
  3. Refaça o programa da Subeção 10.2.2, criando funções para cada ação realizada no arquivo.
  4. Refaça o programa da Subseção 10.2.3 para que grave o nome completo do Funcionário no arquivo.
  5. Refaça o programa da Subseção 10.2.4 para que leia o nome completo do Funcionário do arquivo.
  6. Faça um programa modularizado que manipule um arquivo binário de filmes de uma locadora. Determine os campos necessários para um filme e implemente no seu programa as operações de gravar, buscar, apagar e mostrar os filmes da locadora.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License