ساخت یک تولید کننده کد QR در Rust: قسمت 1

برای چشمی که آموزش ندیده، کدهای QR هالهای از رمز و راز میبافند — نمادهای مخفی که دانش پنهان را پنهان میکنند. با یک اسکن ساده از گوشی شما، این تصاویر معمایی اسرار خود را آشکار می کنند. آنها شما را به وبسایتها منتقل میکنند، اطلاعاتی را ارائه میدهند یا حتی اقدامات خاصی را آغاز میکنند. اما این فناوری جذاب چگونه کار می کند؟ بیایید به دنیای جذاب کدهای QR بپردازیم و اسرار آنها را کشف کنیم.
ملیله QR
در دنیای کدهای QR، تنوع فراوان است. این تصاویر قدرتمند انواع بیشماری دارند که هر کدام دارای ویژگیهای منحصربهفردی هستند. اجازه دهید ابعاد اندازه و رمزگذاری را بررسی کنیم تا بتوانیم درک کنیم.
اندازه
کدهای QR طیفی از اندازهها را شامل میشوند، از نسخه کوچک 1 تا نسخه بزرگ 40. هر نسخه مربوط به تعداد پیکسلهای متفاوتی است.
- نسخه 1 اندازه کدهای QR 21×21 پیکسل است. در حالی که این کدها فشرده هستند، نمی توانند اطلاعات زیادی را در خود نگه دارند. برخی از پیکسل ها برای اهداف خاصی رزرو شده اند و حتی فضای کمتری برای داده ها باقی می گذارند.
- نسخه 40 کدهای QR، در مقابل، 177×177 پیکسل هستند. اینها بوم وسیعی را برای بافتن مقادیر زیادی از اطلاعات ارائه می دهند.
رمزگذاری
چهار حالت رمزگذاری استاندارد شده برای کدهای QR وجود دارد: عددی، الفبایی، بایتی و کانجی.
- حالت عددی ابتدایی ترین است و فقط اعداد را مدیریت می کند. این حالت برای موقعیت هایی که نیاز به انتقال داده های صرفا عددی دارند ایده آل است.
- حالت الفبایی دامنه را گسترش می دهد و حروف کوچک و بزرگ، اعداد و نه کاراکتر خاص را در خود جای می دهد.
- حالت بایت طیف وسیع تری از کاراکترها، از جمله آنهایی که در مجموعه کاراکترهای ISO-8859-1 هستند را پوشش می دهد. این حالت بیشتر کاراکترهای مورد استفاده در زبان های اروپایی را در بر می گیرد.
- حالت کانجی برای اسکریپت ژاپنی طراحی شده است، اما می تواند نویسه های چینی یا کره ای را نیز رمزگذاری کند، مشروط بر اینکه خواننده کد QR برای پردازش آن بر اساس آن مجهز باشد.
مراحل اول
اکنون که به اصول اولیه کدهای QR پرداختیم، بیایید به ساخت کدهای خود بپردازیم و با تعیین نوع کدگذاری بهینه شروع کنیم.
برای انتخاب مناسب ترین نوع رمزگذاری برای یک رشته داده شده، ابتدا باید کاراکترهای منحصر به فرد آن را بررسی کنیم. مشخصات کد QR هر حالت را با مجموعه خاصی از بیت ها مطابقت می دهد، همانطور که در زیر ذکر شده است:
fn get_qr_mode(s: &str) -> &'static str {
let modes = vec![
("0001", r"^[0-9]+$"),
("0010", r"^[A-Za-z0-9 $%*+./:-]+$"),
("0100", r"^[\x00-\xFF]*$"),
("1000", r"^[^\x00-\x7F\xA1-\xDF]*$"),
];
modes.iter()
.find_map(|(mode_str, pattern)| {
let re = Regex::new(pattern).unwrap();
if re.is_match(s) {
Some(*mode_str)
} else {
None
}
})
.unwrap_or("0000")
}
قبل از اینکه بتوانیم اندازه ایده آل را به طور دقیق محاسبه کنیم، ابتدا باید تصحیح خطا را بررسی کنیم.
enum ErrorCorrectionLevel {
L,
M,
Q,
H,
}
تصحیح خطا به عنوان یک حفاظت حیاتی عمل می کند که بازیابی دقیق اطلاعات را حتی زمانی که کد آسیب دیده یا مبهم است، تضمین می کند. این فرآیند از تکنیک Reed-Solomon برای ایجاد داده های اضافی در پیام اصلی استفاده می کند. سطح حفاظت انتخاب شده (L، M، Q یا H) میزان داده های اضافی را که به کد QR تزریق می شود را تعیین می کند. سطوح بالاتر حفاظت، تصحیح خطای قویتری را ارائه میکنند، اما به فضای بیشتری در کد نیاز دارند، در حالی که سطوح پایینتر فضا را حفظ میکنند اما سپر متوسطتری را ارائه میدهند. در نتیجه، کدهای QR همچنان دانش پنهان خود را با دقت آشکار میکنند، حتی زمانی که نشانههای آسیب یا ابهام را در خود دارند، و تصحیح خطا را به یک جنبه ضروری از عملکرد خود تبدیل میکنند.
اکنون می توانیم ظرفیت ها را محاسبه کنیم:
fn get_qr_data_capacity(version: u32, ecl: ErrorCorrectionLevel) -> u32 {
// Data capacities for QR code versions 1-40
// Source: https://www.qrcode.com/en/about/version.html
let capacities = [
[19, 16, 13, 9], [34, 28, 22, 16], [55, 44, 34, 26], [80, 64, 48, 36],
[108, 86, 62, 46], [136, 108, 86, 60], [156, 124, 100, 72], [194, 154, 122, 86],
[232, 182, 152, 106], [274, 216, 180, 130], [324, 254, 210, 154], [370, 290, 244, 178],
[428, 334, 292, 207], [461, 365, 308, 235], [523, 415, 348, 267], [589, 453, 390, 295],
[647, 507, 434, 325], [721, 563, 486, 367], [795, 627, 554, 399], [861, 669, 604, 439],
[932, 714, 652, 462], [1006, 782, 674, 496], [1094, 860, 746, 528], [1174, 914, 808, 574],
[1276, 1000, 870, 610], [1370, 1062, 952, 666], [1468, 1128, 1020, 711], [1531, 1193, 1051, 751],
[1631, 1267, 1141, 805], [1735, 1373, 1225, 868], [1843, 1455, 1313, 908], [1955, 1541, 1409, 982],
[2071, 1631, 1501, 1030], [2191, 1725, 1601, 1112], [2306, 1812, 1700, 1168], [2434, 1914, 1828, 1228],
[2566, 1992, 1921, 1283], [2702, 2102, 2051, 1351], [2812, 2216, 2180, 1421], [2956, 2334, 2303, 1500],
];
match ecl {
ErrorCorrectionLevel::L => capacities[version as usize - 1][0],
ErrorCorrectionLevel::M => capacities[version as usize - 1][1],
ErrorCorrectionLevel::Q => capacities[version as usize - 1][2],
ErrorCorrectionLevel::H => capacities[version as usize - 1][3],
}
}
این تابع داده ها را از بردار حاوی دقیق ترین اعداد برای ظرفیت ها می کشد.
اکنون می توانیم نسخه کد QR مناسب را شناسایی کنیم:
fn get_qr_version(input_length: usize, mode: &str, ecl: ErrorCorrectionLevel) -> Option<u32> {
(1..=40).find_map(|version| {
let capacity = get_qr_data_capacity(version, ecl);
let capacity = match mode {
"0001" => capacity * 10 / 3, // Numeric
"0010" => capacity * 5 / 3, // Alphanumeric
"0100" => capacity, // Byte
"1000" => capacity * 2 / 3, // Kanji
_ => return None,
};
if input_length <= capacity as usize {
Some(version)
} else {
None
}
})
}
با این مراحل اولیه، ما فرآیند ایجاد یک کد QR را آغاز کرده ایم:
fn main() {
let input = "Hello, QR Code World!";
let mode = get_qr_mode(input);
let ecl = ErrorCorrectionLevel::L;
let input_length = match mode {
"0001" => input.chars().count(),
"0010" => input.chars().count(),
"0100" => input.as_bytes().len(),
"1000" => input.chars().count(),
_ => panic!("Invalid QR mode"),
};
let version = get_qr_version(input_length, mode, ecl).unwrap_or_else(|| {
panic!("Input data too long for any QR code version")
});
println!("QR Code Version: {}", version);
}
ما با موفقیت حالت را تعیین کردهایم و متعاقباً نسخه مناسب را برای یک سطح تصحیح خطا مشخص کردهایم، و یک پایه محکم برای ایجاد کد QR خود ایجاد کردهایم.