TC kimlik numarası neden "checksum"lı bir numaradır?
Türkiye Cumhuriyeti Kimlik Numarası (TCKN), Nüfus ve Vatandaşlık İşleri Genel Müdürlüğü (NVİ) tarafından MERNİS sistemi üzerinden her vatandaşa bir kez atanan 11 haneli bir numaradır. Günlük konuşmada "TC kimlik no" ya da kısaca "TC" olarak geçer. Numaranın son iki hanesi rastgele değildir; ilk dokuz hanenin matematiksel fonksiyonu olarak hesaplanır. Bu tasarım, form üzerinde tek bir rakam yanlış yazıldığında numaranın büyük olasılıkla geçersiz hâle gelmesini sağlar — yani yerel bir "ilk seviye" doğrulama katmanı kurar.
Bu yazıda algoritmanın matematiksel mantığını adım adım çözeceğiz, tipik implementasyon tuzaklarını göstereceğiz ve beş programlama dilinde çalışan referans kodlar sunacağız. Amaç sadece formülü kopyala-yapıştır yapmak değil; TC kimlik numarasının arkasındaki tasarım tercihini de anlamak. Böylece üretim kodunuzda karşılaşacağınız uç durumlarda (leading zero, unicode rakam, trim eksikliği) algoritmanın nerede kırıldığını da göreceksiniz. Test verisi için kendi üretim rutininizi yazabilir ya da zaman kazanmak için hazır bir tc uret aracı kullanabilirsiniz.
Hemen belirtelim: bu formül sadece format doğrulaması yapar. Numaranın gerçek bir vatandaşa ait olup olmadığını ancak NVİ'nin TCKimlikNoDogrula servisi söyler. Bu ayrımı netleştirmek için NVİ servisi rehberini okumanızı öneririz.
TC kimlik algoritmasının resmi tanımı
TCKN'nin 11 hanesini d[0] d[1] d[2] ... d[10] olarak düşünelim (sıfır indeksli). Kurallar şunlar:
d[0] != 0. İlk hane sıfır olamaz.d[9](onuncu hane, kontrol hanesi):((d[0]+d[2]+d[4]+d[6]+d[8]) * 7 - (d[1]+d[3]+d[5]+d[7])) mod 10d[10](on birinci hane, toplam kontrol):(d[0]+d[1]+...+d[9]) mod 10
Tek indeksli (1 tabanlı) resmi dokümanlarda genelde şöyle yazılır: "1., 3., 5., 7., 9. hanelerin toplamının 7 katından, 2., 4., 6., 8. hanelerin toplamı çıkarılır; sonucun 10'a göre modu 10. haneyi verir." Aynı ifadedir, sadece indeksleme farklıdır. Koda dökerken dikkat edin.
Tek rakamlı bir örnek
10000000146 numarasını ele alalım:
- Tek indeksli haneler (0, 2, 4, 6, 8):
1 + 0 + 0 + 0 + 0 = 1 - Çift indeksli haneler (1, 3, 5, 7):
0 + 0 + 0 + 0 = 0 d[9]adayı:(1 * 7 - 0) mod 10 = 7
Ancak örnek numarada d[9] = 4. Demek ki bu TC kimlik no geçersiz. Geçerli olması için d[9] hanesinin 7 olması gerekirdi. Bu hızlı kontrol, üretim kodunda birim test yazarken pratik.
Negatif mod tuzağı
Formülde çıkarma işlemi var. (sumOdd * 7 - sumEven) negatif çıkabilir mi? Teorik olarak evet: sumOdd = 0, sumEven = 36 gibi. Ancak d[0] != 0 kısıtı ve rakamların 0-9 aralığında olması birlikte düşünüldüğünde pratikte nadirdir; yine de kodunuz bu durumu ele almalı.
C ve Java gibi dillerde % operatörü negatif sonuç verebilir (-3 % 10 == -3). Python'da ise -3 % 10 == 7. Diller arası taşıma yaparken bu farkı unutmayın. Savunmacı yazmak gerekirse:
let digit10 = ((sumOdd * 7) - sumEven) % 10;
if (digit10 < 0) digit10 += 10;TC Kimlik Numarasının tasarım mantığı
Neden 7 ile çarpma? Neden 10'a mod? Kısa cevap: bu, "ISO 7064 benzeri" bir checksum şemasıdır. Küçük rakamları büyüterek pozisyonel hataları (iki rakamın yer değiştirmesi, tek bir rakamın yanlış yazılması) yüksek olasılıkla yakalar.
7 asal bir sayıdır ve 10 ile aralarında asaldır; bu sayede 7x mod 10 fonksiyonu 0-9 aralığında bire bir (bijeksiyon) davranır. {0,1,2,...,9} kümesini {0,7,4,1,8,5,2,9,6,3} kümesine eşler. Sonuç: tek bir rakamdaki değişiklik kontrol hanesinde mutlaka bir değişiklik yaratır. Eğer 7 yerine 2 seçilseydi (2 ile 10 aralarında asal değil), bazı hatalar yakalanmazdı.
On birinci hane (d[10]) basit bir toplam-mod-10'dur ve ikinci bir güvenlik ağı görevi görür. Özellikle iki rakamın yer değiştirmesi (transpozisyon) hatalarını yakalar.
Yaygın implementasyon hataları
Aşağıdaki hatalar production code review'lerinde sürekli karşımıza çıkar:
- String değil de number tipinde taşımak:
02345678901gibi leading zero içeren bir TC number'a cast edilince2345678901olur ve 10 haneye düşer. TCKN her zamanstringolarak işlenmeli. Zaten algoritma gereğid[0] != 0ama veri kaynağından (CSV, Excel) gelirken leading zero korunsun diye string kalmalı. - Regex'in §§T0§§ karakter sınıfı: JavaScript'te varsayılan olarak
\dsadece ASCII 0-9'u yakalar, ama Python 3'tere.UNICODEbayrağı açıkken Arap-Hint, Bengal ve Devanagari rakamlarını da yakalar. Bu istenmeyen bir davranış. Güvenli seçim:[0-9]{11}. - Trim ve whitespace: Kullanıcı formda " 12345678901 " yazarsa
length === 11kontrolü patlar.input.trim()ilk satırdan önce gelir. - Negatif mod: Yukarıda anlattık. JavaScript ve Java için
((x % 10) + 10) % 10kalıbı güvenlidir. - İlk hane kontrolünü atlamak: "00000000000" tüm checksum testlerinden geçer ama geçersiz bir TC kimlik numarasıdır.
d[0] != 0şartı ayrıca eklenmeli.
Bu tuzakların daha geniş bir listesi için TCKN/VKN doğrulamada sık yapılan hatalar yazımıza bakın.
Referans implementasyonlar
Aşağıda beş dilde minimal ama production-safe doğrulayıcılar yer alıyor. Her biri aynı sözleşmeyi takip eder: string girdi, boolean çıktı, hiçbir exception fırlatmaz — geçersiz girdi false döner.
JavaScript (ES2022+)
export function isValidTckn(input) {
if (typeof input !== 'string') return false;
const tckn = input.trim();
if (!/^[1-9][0-9]{10}$/.test(tckn)) return false;
const d = [...tckn].map(Number);
const sumOdd = d[0] + d[2] + d[4] + d[6] + d[8];
const sumEven = d[1] + d[3] + d[5] + d[7];
const digit10 = ((sumOdd * 7 - sumEven) % 10 + 10) % 10;
const digit11 = (sumOdd + sumEven + digit10) % 10;
return d[9] === digit10 && d[10] === digit11;
}Python (3.10+)
import re
_TCKN_RE = re.compile(r"^[1-9][0-9]{10}$")
def is_valid_tckn(value: str) -> bool:
if not isinstance(value, str):
return False
tckn = value.strip()
if not _TCKN_RE.match(tckn):
return False
d = [int(ch) for ch in tckn]
sum_odd = d[0] + d[2] + d[4] + d[6] + d[8]
sum_even = d[1] + d[3] + d[5] + d[7]
digit10 = (sum_odd * 7 - sum_even) % 10
digit11 = (sum_odd + sum_even + digit10) % 10
return d[9] == digit10 and d[10] == digit11Go (1.20+)
package tckn
import "regexp"
var tcknRe = regexp.MustCompile(`^[1-9][0-9]{10}$`)
func IsValid(input string) bool {
if !tcknRe.MatchString(input) {
return false
}
var d [11]int
for i := 0; i < 11; i++ {
d[i] = int(input[i] - '0')
}
sumOdd := d[0] + d[2] + d[4] + d[6] + d[8]
sumEven := d[1] + d[3] + d[5] + d[7]
digit10 := ((sumOdd*7-sumEven)%10 + 10) % 10
digit11 := (sumOdd + sumEven + digit10) % 10
return d[9] == digit10 && d[10] == digit11
}Java (17+)
import java.util.regex.Pattern;
public final class Tckn {
private static final Pattern RE = Pattern.compile("^[1-9][0-9]{10}$");
public static boolean isValid(String input) {
if (input == null) return false;
String tckn = input.strip();
if (!RE.matcher(tckn).matches()) return false;
int[] d = new int[11];
for (int i = 0; i < 11; i++) d[i] = tckn.charAt(i) - '0';
int sumOdd = d[0] + d[2] + d[4] + d[6] + d[8];
int sumEven = d[1] + d[3] + d[5] + d[7];
int digit10 = Math.floorMod(sumOdd * 7 - sumEven, 10);
int digit11 = (sumOdd + sumEven + digit10) % 10;
return d[9] == digit10 && d[10] == digit11;
}
}Math.floorMod kullanımı Java'daki negatif mod sorununu çözer.
C# (.NET 8+)
using System.Text.RegularExpressions;
public static class Tckn
{
private static readonly Regex Re = new(@"^[1-9][0-9]{10}$", RegexOptions.Compiled);
public static bool IsValid(string? input)
{
if (string.IsNullOrWhiteSpace(input)) return false;
var tckn = input.Trim();
if (!Re.IsMatch(tckn)) return false;
Span<int> d = stackalloc int[11];
for (int i = 0; i < 11; i++) d[i] = tckn[i] - '0';
int sumOdd = d[0] + d[2] + d[4] + d[6] + d[8];
int sumEven = d[1] + d[3] + d[5] + d[7];
int digit10 = ((sumOdd * 7 - sumEven) % 10 + 10) % 10;
int digit11 = (sumOdd + sumEven + digit10) % 10;
return d[9] == digit10 && d[10] == digit11;
}
}Test stratejisi
Her implementasyonun en azından şu vakaları kapsaması gerekir:
- Bilinen geçerli bir TC kimlik numarası (üretici ile elde edilmiş)
- İlk hanesi 0 olan input
- 10 veya 12 haneli input
- Boş string, null, whitespace
- Harf içeren input
- Unicode rakamlar (
"١٢٣٤٥٦٧٨٩٠١"gibi) - Son haneyi +1 yaparak bozulmuş geçerli bir TC kimlik no
Test verisi üretimi için TC Üretici aracını veya kendi üretim fonksiyonunuzu kullanın. Bu konuda test verisi üretiminde en iyi pratikler yazımızda derin bir kapsam var.
Performans notu
11 hane üzerinde sabit sayıda aritmetik işlem yapıldığı için her bir doğrulama O(1). Modern bir x86 CPU'da yaklaşık 50-200 nanosaniye arasında tamamlanır — yani saniyede milyonlarca TC kimlik numarasını doğrulayabilirsiniz. Bu nedenle darboğaz neredeyse her zaman network, DB veya serialization katmanıdır, aritmetik değil. Yüksek hacimli senaryolar için toplu doğrulama rehberine göz atın.
Sonuç ve ileri okuma
TC kimlik algoritması basittir ama birçok implementasyonda aynı hatalar tekrarlanır: string/number karışıklığı, unicode regex, negatif mod, ilk hane kontrolü eksikliği. Yukarıdaki beş dildeki referans kodları kendi kod tabanınıza uyarlayabilirsiniz; testlerin eksiksiz olduğundan emin olun.
- VKN algoritmasının benzer bir analizini arıyorsanız: VKN yapısı ve doğrulama
- Gerçek kimlik doğrulaması yapıyorsanız: NVİ servisi ile TCKN doğrulama
- Kişisel veri boyutu için: KVKK uyumlu kimlik doğrulama
Aracı hemen deneyin: TC Doğrulayıcı ve TC Üretici.