Coverage for presidio_analyzer / predefined_recognizers / country_specific / germany / de_kfz_recognizer.py: 100%

9 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-29 09:03 +0000

1from typing import List, Optional 

2 

3from presidio_analyzer import Pattern, PatternRecognizer 

4 

5 

6class DeKfzRecognizer(PatternRecognizer): 

7 """ 

8 Recognizes German vehicle registration plates (KFZ-Kennzeichen). 

9 

10 German license plates are issued by local Zulassungsbehörden (vehicle registration 

11 authorities). While not exclusively personal (vehicles can be owned by companies), 

12 they can be linked to natural persons and are considered personally identifiable 

13 information in the context of data protection law. 

14 

15 Legal basis: Fahrzeug-Zulassungsverordnung (FZV) § 8, § 9. 

16 Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten) – license plates 

17 constitute personal data when they can be linked to an identifiable person (ECJ 

18 ruling C-582/14, Breyer v. Germany). 

19 

20 Format: 

21 [Unterscheidungszeichen] [Erkennungszeichen] [Ziffern] [Suffix] 

22 

23 Unterscheidungszeichen: 1–3 uppercase letters (district/city code) 

24 Erkennungszeichen: 1–2 uppercase letters 

25 Ziffern: 1–4 digits 

26 Suffix (optional): E (electric vehicle) or H (Oldtimer/historic, ≥30 years) 

27 

28 Examples 

29 B AB 1234 (Berlin) 

30 M XY 999 (München) 

31 HH AB 1234 (Hamburg) 

32 KA EF 1H (Karlsruhe, historic) 

33 MIL E 1234E (Miltenberg, electric) 

34 S AB 12 (Stuttgart) 

35 

36 Note: Seasonal plates (Saison-Kennzeichen) also contain month ranges but are not 

37 covered by this pattern. 

38 

39 :param patterns: List of patterns to be used by this recognizer 

40 :param context: List of context words to increase confidence in detection 

41 :param supported_language: Language this recognizer supports 

42 :param supported_entity: The entity this recognizer can detect 

43 """ 

44 

45 PATTERNS = [ 

46 Pattern( 

47 "KFZ-Kennzeichen (mit Leerzeichen)", 

48 r"(?<![\w-])[A-ZÄÖÜ]{1,3}\s[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)", 

49 0.3, 

50 ), 

51 Pattern( 

52 "KFZ-Kennzeichen (mit Bindestrich)", 

53 r"(?<![\w-])[A-ZÄÖÜ]{1,3}-[A-Z]{1,2}-\d{1,4}[EH]?(?!\w)", 

54 0.3, 

55 ), 

56 Pattern( 

57 "KFZ-Kennzeichen (Bindestrich + Leerzeichen)", 

58 r"(?<![\w-])[A-ZÄÖÜ]{1,3}-[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)", 

59 0.3, 

60 ), 

61 Pattern( 

62 "KFZ-Kennzeichen (ASCII only, mit Leerzeichen)", 

63 r"(?<![\w-])[A-Z]{1,3}\s[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)", 

64 0.2, 

65 ), 

66 Pattern( 

67 "KFZ-Kennzeichen (ASCII only, Bindestrich + Leerzeichen)", 

68 r"(?<![\w-])[A-Z]{1,3}-[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)", 

69 0.2, 

70 ), 

71 ] 

72 

73 CONTEXT = [ 

74 "kennzeichen", 

75 "kfz-kennzeichen", 

76 "kraftfahrzeugkennzeichen", 

77 "nummernschild", 

78 "fahrzeugkennzeichen", 

79 "zulassung", 

80 "kfz", 

81 "fahrzeug", 

82 "auto", 

83 "pkw", 

84 "lkw", 

85 "fahrzeugschein", 

86 "fahrzeugbrief", 

87 "zulassungsbescheinigung", 

88 "amtliches kennzeichen", 

89 ] 

90 

91 def __init__( 

92 self, 

93 patterns: Optional[List[Pattern]] = None, 

94 context: Optional[List[str]] = None, 

95 supported_language: str = "de", 

96 supported_entity: str = "DE_KFZ", 

97 name: Optional[str] = None, 

98 ): 

99 patterns = patterns if patterns else self.PATTERNS 

100 context = context if context else self.CONTEXT 

101 super().__init__( 

102 supported_entity=supported_entity, 

103 patterns=patterns, 

104 context=context, 

105 supported_language=supported_language, 

106 name=name, 

107 )