C# - Calculando o CRC de strings e arquivos


O CRC - Cyclic redundancy check, ou verificação de redundância cíclica é um código detector de erros cujo algoritmo é muito usado em protocolos de comunicação diferentes, empacotamento e desempacotamento de algoritmos para garantir a robustez dos dados.

A idéia por trás do CRC é simples - calcular um checksum original (seqüência de verificação de quadros) para cada quadro de dados, baseada em seu conteúdo e colar o checksum no final de cada mensagem. Uma vez que os dados são recebidos, é possível realizar o mesmo cálculo e comparar os resultados - se os resultados são semelhantes, a mensagem é válida.

O CRC é calculado e anexado na informação a transmitir (ou armazenar) sendo verificado após a recepção ou acesso, para confirmar se não ocorreram alterações

O CRC é popular por ser simples de implementar em hardware binário, simples de ser analisado matematicamente, e pela eficiência em detectar erros típicos causados por ruído em canais de transmissão.

A utilidade do CRC advém das seguintes propriedades:

1. Como todos os bits são usados no cálculo do CRC, a mudança em apenas um bit provoca uma mudança no CRC.
2. Mesmo mudanças pequenas nos dados levam a CRCs muito diferentes. Experiências com o CRC-32 (usando polinômios de 32 bits) mostram que é muito raro que a introdução de erros nos dados não seja detectado pelo CRC.
3. A probabilidade de qualquer dos 232 valores possíveis para o CRC é praticamente uniforme.

Existem diferentes tipos de CRC que podem ser calculados: CRC-32, CRC-16, CRC-12, CRC-8, etc.

Para realizarmos cálculos envolvendo o CRC temos que utilizar o namespace System.Security.Cryptography e neste artigo eu ou calcular o CRC de strings e arquivos.

Vamos abrir o Visual C# 2010 Express Edition e no menu File-> New Poject, selecione o Template Windows Forms Applicacion com o nome Calculando_CRC;

No menu Project -> Add Class inclua uma classe com o nome CrcStream com o seguinte código:

using System.IO;

namespace Macoratti
{
   
/// <summary>
    /// Encapsula um <see cref="System.IO.Stream" /> para calcular o checksum CRC32
    /// em tempo de execução
    /// </summary>

    public class CrcStream : Stream
    {
       
/// <summary>
        /// Encapsula um <see cref="System.IO.Stream" />.
        /// </summary>
        /// <param name="stream">O stream para calcular o checksum.</param>

        public CrcStream(Stream stream)
        {
            this.stream = stream;
        }

        Stream stream;

      
 /// <summary>
        /// Obtem o stream.
        /// </summary>

        public Stream Stream
        {
            get { return stream; }
        }

        public override bool CanRead
        {
            get { return stream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return stream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return stream.CanWrite; }
        }

        public override void Flush()
        {
            stream.Flush();
        }

        public override long Length
        {
            get { return stream.Length; }
        }

        public override long Position
        {
            get
            {
                return stream.Position;
            }
            set
            {
                stream.Position = value;
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return stream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            stream.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            count = stream.Read(buffer, offset, count);
            readCrc = CalculateCrc(readCrc, buffer, offset, count);
            return count;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            stream.Write(buffer, offset, count);

            writeCrc = CalculateCrc(writeCrc, buffer, offset, count);
        }

        uint CalculateCrc(uint crc, byte[] buffer, int offset, int count)
        {
            unchecked
            {
                for (int i = offset, end = offset + count; i < end; i++)
                    crc = (crc >> 8) ^ table[(crc ^ buffer[i]) & 0xFF];
            }
            return crc;
        }

        static private uint[] table = GenerateTable();

        static private uint[] GenerateTable()
        {
            unchecked
            {
                uint[] table = new uint[256];

                uint crc;
                const uint poly = 0xEDB88320;
                for (uint i = 0; i < table.Length; i++)
                {
                    crc = i;
                    for (int j = 8; j > 0; j--)
                    {
                        if ((crc & 1) == 1)
                            crc = (crc >> 1) ^ poly;
                        else
                            crc >>= 1;
                    }
                    table[i] = crc;
                }
                return table;
            }
        }

        uint readCrc = unchecked(0xFFFFFFFF);

    
   /// <summary>
        /// Obtem o checksum CRC dos dados que foram lidos pelo stream
        /// </summary>

        public uint ReadCrc
        {
            get { return unchecked(readCrc ^ 0xFFFFFFFF); }
        }

        uint writeCrc = unchecked(0xFFFFFFFF);

       
/// <summary>
        /// Obtem o checksum CRC dos dados que foram escritos para o stream
        /// </summary>

        public uint WriteCrc
        {
            get { return unchecked(writeCrc ^ 0xFFFFFFFF); }
        }

     
  /// <summary>
        /// Reseta a leitura e escrita dos checksums.
        /// </summary>

        public void ResetChecksum()
        {
            readCrc = unchecked(0xFFFFFFFF);
            writeCrc = unchecked(0xFFFFFFFF);
        }
    }
}
autor : 
http://www.codeproject.com/Members/reinux

Agora vamos definir no formulário form1.cs uma interface bem simples onde iremos informar o nome do arquivo e calcular o seu CRC.

Abaixo vemos o formulário que usa os controles TextBox e Button;

No evento Click do botão de comando - Calcula CRC - vamos incluir o código que usa a classe CrcStream para calcular o CRC do arquivo:

using System;
using System.Windows.Forms;
using System.IO;
using Macoratti;

namespace Calculando_CRC
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        FileStream file = null;
        CrcStream stream = null;

        private void btnCalculaCRC_Click(object sender, EventArgs e)
        {
            if (txtArquivo.Text == string.Empty)
            {
                MessageBox.Show("Informe o nome do arquivo.");
                return;
            }

            string arquivo = txtArquivo.Text;

            //Abre um fluxo de stream e o encapsula em um CrcStream
            try
            {
                file = new FileStream(arquivo, FileMode.Open);
                stream = new CrcStream(file);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erro ao acessar o arquivo :  " + ex.Message);
                return;
            }

            //Usa o arquivo - neste caso le o arquivo como uma string
            StreamReader reader = new StreamReader(stream);
            string texto = reader.ReadToEnd();

            //Imprime o checksum calculado
            txtCRC.Text = stream.ReadCrc.ToString("X8");
        }
    }
}

Executando o projeto iremos obter para um determinado arquivo informado:

Pegue o projeto completo aqui: Calculando_CRC.zip

Romanos 2:2 E bem sabemos que o juízo de Deus é segundo a verdade, contra os que tais coisas praticam.
Romanos 2:3
E tu, ó homem, que julgas os que praticam tais coisas, cuidas que, fazendo-as tu, escaparás ao juízo de Deus?
Romanos 2:4
Ou desprezas tu as riquezas da sua benignidade, e paciência e longanimidade, ignorando que a benignidade de Deus te conduz ao arrependimento?
Romanos 2:5
Mas, segundo a tua dureza e teu coração impenitente, entesouras ira para ti no dia da ira e da revelação do justo juízo de Deus,

Romanos 2:6
que retribuirá a cada um segundo as suas obras;

Referências:


José Carlos Macoratti