/// Extracts a 17-character VIN from OCR'd text (a photo of a door-jamb /// sticker, dashboard plate, title, etc). /// /// Real VINs never contain the letters I, O, or Q (they're excluded from /// the standard specifically so they can't be confused with 1 and 0), so /// requiring that charset both matches genuine VINs and rules out a lot of /// incidental 17-character noise elsewhere on the sticker (barcodes text, /// weight ratings, date codes, etc). class VinParser { static final _labeledVinPattern = RegExp(r'VIN[:\s]*([A-HJ-NPR-Z0-9]{17})\b', caseSensitive: false); // \b on both sides matters: since digits and letters are both "word" // characters, this only matches a maximal run of exactly 17 eligible // characters — not a 17-character slice out of an 18+ character run. static final _bareVinPattern = RegExp(r'\b([A-HJ-NPR-Z0-9]{17})\b', caseSensitive: false); /// Returns the VIN in uppercase, or null if nothing matching the VIN /// charset/length was found. A labeled "VIN: ..." match is preferred /// over a bare 17-character token, in case a busy sticker has more than /// one candidate (e.g. also a 17-digit tire/parts barcode number). static String? parse(String text) { final labeled = _labeledVinPattern.firstMatch(text); if (labeled != null) return labeled.group(1)!.toUpperCase(); final bare = _bareVinPattern.firstMatch(text); if (bare != null) return bare.group(1)!.toUpperCase(); return null; } }