Last active
January 3, 2019 21:16
-
-
Save luluco250/29c8584d4d481d8c3df8843bbe1ac4bf to your computer and use it in GitHub Desktop.
Brazilian ID Document (CPF) Data Type for C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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