Skip to content

Instantly share code, notes, and snippets.

@luluco250
Last active January 3, 2019 21:16
Show Gist options
  • Save luluco250/29c8584d4d481d8c3df8843bbe1ac4bf to your computer and use it in GitHub Desktop.
Save luluco250/29c8584d4d481d8c3df8843bbe1ac4bf to your computer and use it in GitHub Desktop.
Brazilian ID Document (CPF) Data Type for C#
using System;
using System.Text.RegularExpressions;
public struct CPF {
public static readonly Regex FormatRegex = new Regex(
@"(\d\d\d)\.(\d\d\d)\.(\d\d\d)-(\d\d)",
RegexOptions.Compiled
);
public const int DigitCount = 11;
byte _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10;
public CPF(
byte _0, byte _1, byte _2,
byte _3, byte _4, byte _5,
byte _6, byte _7, byte _8,
byte _9, byte _10
) {
this._0 = _0;
this._1 = _1;
this._2 = _2;
this._3 = _3;
this._4 = _4;
this._5 = _5;
this._6 = _6;
this._7 = _7;
this._8 = _8;
this._9 = _9;
this._10 = _10;
}
public CPF(
string first,
string second,
string third,
string validator
) : this(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) {
Build(first, second, third, validator);
}
public CPF(string cpf) : this(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) {
Build(cpf);
}
public byte this[int i] {
get {
switch (i) {
case 0:
return _0;
case 1:
return _1;
case 2:
return _2;
case 3:
return _3;
case 4:
return _4;
case 5:
return _5;
case 6:
return _6;
case 7:
return _7;
case 8:
return _8;
case 9:
return _9;
case 10:
return _10;
default:
throw new ArgumentOutOfRangeException();
}
}
}
static byte CharDigitToByte(char digit) {
if (digit >= '0' && digit <= '9')
return (byte)(digit - '0');
throw new ArgumentException("Not a digit, cannot convert to byte.");
}
void Build(
string first,
string second,
string third,
string validator
) {
if (
first.Length != 3 ||
second.Length != 3 ||
third.Length != 3 ||
validator.Length != 2
) {
throw new ArgumentException(
"Invalid strings for building a CPF: 'first', 'second' " +
"and 'third' must be length == 3, 'validator' must be " +
$"length == 2. Got lengths {first.Length}, {second.Length}, " +
$"{third.Length} and {validator.Length}."
);
}
try {
_0 = CharDigitToByte(first[0]);
_1 = CharDigitToByte(first[1]);
_2 = CharDigitToByte(first[2]);
_3 = CharDigitToByte(second[0]);
_4 = CharDigitToByte(second[1]);
_5 = CharDigitToByte(second[2]);
_6 = CharDigitToByte(third[0]);
_7 = CharDigitToByte(third[1]);
_8 = CharDigitToByte(third[2]);
_9 = CharDigitToByte(validator[0]);
_10 = CharDigitToByte(validator[1]);
} catch (ArgumentException) {
throw new ArgumentException(
$"CPF strings must only contain digits, got '{first}.{second}" +
$".{third}-{validator}'."
);
}
}
void Build(string cpf) {
var match = FormatRegex.Match(cpf);
if (!match.Success)
throw new ArgumentException("Invalid CPF format!");
Build(
match.Groups[1].ToString(),
match.Groups[2].ToString(),
match.Groups[3].ToString(),
match.Groups[4].ToString()
);
}
public bool Validate() {
int a, b;
// Calculate first sum.
a =
_0 * 10 +
_1 * 9 +
_2 * 8 +
_3 * 7 +
_4 * 6 +
_5 * 5 +
_6 * 4 +
_7 * 3 +
_8 * 2;
// Get remainder of the sum.
b = a % 11;
// Get verifier as 0 if (remainder < 2) else (11 - remainder).
a = (b < 2) ? 0 : 11 - b;
// Quit early if the first verifier digit is wrong.
if (a != _9)
return false;
// Calculate second sum.
a =
_0 * 11 +
_1 * 10 +
_2 * 9 +
_3 * 8 +
_4 * 7 +
_5 * 6 +
_6 * 5 +
_7 * 4 +
_8 * 3 +
_9 * 2;
// Get remainder of the sum.
b = a % 11;
// Get verifier as 0 if (remainder < 2) else (11 - remainder).
a = (b < 2) ? 0 : 11 - b;
// Return whether the second verifier is correct or not.
return a == _10;
}
public override string ToString() {
return $"{_0}{_1}{_2}.{_3}{_4}{_5}.{_6}{_7}{_8}-{_9}{_10}";
}
public byte[] AsByteArray() {
return new byte[DigitCount] {
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10
};
}
public static CPF FromByteArray(byte[] b) {
if (b.Length != DigitCount)
throw new ArgumentException(
$"Byte array must have exactly {DigitCount} positions " +
$", got {b.Length}."
);
if (
b[0] > 9 || b[0] < 0 ||
b[1] > 9 || b[1] < 0 ||
b[2] > 9 || b[2] < 0 ||
b[3] > 9 || b[3] < 0 ||
b[4] > 9 || b[4] < 0 ||
b[5] > 9 || b[5] < 0 ||
b[6] > 9 || b[6] < 0 ||
b[7] > 9 || b[7] < 0 ||
b[8] > 9 || b[8] < 0 ||
b[9] > 9 || b[9] < 0 ||
b[10] > 9 || b[10] < 0
)
throw new ArgumentOutOfRangeException(
"No bytes in the array can exceed a single digit [0-9]."
);
CPF cpf;
cpf._0 = b[0];
cpf._1 = b[1];
cpf._2 = b[2];
cpf._3 = b[3];
cpf._4 = b[4];
cpf._5 = b[5];
cpf._6 = b[6];
cpf._7 = b[7];
cpf._8 = b[8];
cpf._9 = b[9];
cpf._10 = b[10];
return cpf;
}
public long AsLong() {
return
_0 * 10000000000L +
_1 * 1000000000L +
_2 * 100000000L +
_3 * 10000000L +
_4 * 1000000L +
_5 * 100000L +
_6 * 10000L +
_7 * 1000L +
_8 * 100L +
_9 * 10L +
_10;
}
public static CPF FromLong(long l) {
if (l > 99999999999L || l < 0L)
throw new ArgumentOutOfRangeException();
CPF cpf;
cpf._0 = (byte)((l / 10000000000L) % 10L);
cpf._1 = (byte)((l / 1000000000L) % 10L);
cpf._2 = (byte)((l / 100000000L) % 10L);
cpf._3 = (byte)((l / 10000000L) % 10L);
cpf._4 = (byte)((l / 1000000L) % 10L);
cpf._5 = (byte)((l / 100000L) % 10L);
cpf._6 = (byte)((l / 10000L) % 10L);
cpf._7 = (byte)((l / 1000L) % 10L);
cpf._8 = (byte)((l / 100L) % 10L);
cpf._9 = (byte)((l / 10L) % 10L);
cpf._10 = (byte)(l % 10L);
return cpf;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment