31 lines
1.4 KiB
Dart
31 lines
1.4 KiB
Dart
/// 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;
|
|
}
|
|
}
|