ISBN Verifier
1. Readme
ISBN 检验器
这个ISBN-10 检测进程用于验证图书识别号。通常包含破折号,看起来像:3-598-21508-8
国际标准书号
ISBN-10 格式是 9 位数字(0 到 9)加上一个校验字符(一个数字或一个 X)。在校验字符为 X 的情况下,这表示值”10”。这些可以与连字符(不管有没有)通信,并且可以通过以下公式检查它们的有效性:
(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0
如果结果是 0,那么它是一个有效的 ISBN-10,否则它是无效的.
例子
让我们用 ISBN-103-598-21508-8
. 我们把它插入到公式中,得到:
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
由于结果是 0,这证明我们的 ISBN 是有效的.
任务
给定一个字符串,程序应该检查所提供的字符串是否是有效的 ISBN-10。为了实现这一点,需要在计算 ISBN 的校验位数之前,考虑字符串的预处理/解析.
该程序应该能够验证 ISBN-10 的破折号(不管有没有).
告诫
在某些语言中,从字符串转换为数字可能是棘手的。现在,甚至更棘手的是,ISBN-10 的校验位可能是”X”(表示”10”).例如3-598-21507-X
是一个有效的 ISBN-10.
奖金任务
-
从输入 ISBN-10 生成有效的 ISBN-13(并且可能用类似验证器再次验证它).
-
生成有效的 ISBN,甚至可能从给定的起始 ISBN 中生成.
资源
将一个字符串,转换成一个数字和一些基本的处理,利用一个可靠的真实世界的例子.https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation
2. 开始你的表演
/// Determines whether the supplied string is a valid ISBN number pub fn is_valid_isbn(isbn: &str) -> bool { unimplemented!("Is {:?} a valid ISBN number?", isbn); }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[test] fn test_valid() { assert!(is_valid_isbn("3-598-21508-8")); } #[test] //#[ignore] fn test_invalid_check_digit() { assert!(!is_valid_isbn("3-598-21508-9")); } #[test] //#[ignore] fn test_valid_check_digit_of_10() { assert!(is_valid_isbn("3-598-21507-X")); } #[test] //#[ignore] fn test_invalid_character_as_check_digit() { assert!(!is_valid_isbn("3-598-21507-A")); } #[test] //#[ignore] fn test_invalid_character_in_isbn() { assert!(!is_valid_isbn("3-598-2K507-0")); } #[test] //#[ignore] #[allow(non_snake_case)] fn test_invalid_isbn_with_invalid_X() { assert!(!is_valid_isbn("3-598-2X507-9")); } #[test] //#[ignore] fn test_valid_isbn_without_dashes() { assert!(is_valid_isbn("3598215088")); } #[test] //#[ignore] #[allow(non_snake_case)] fn test_valid_isbn_without_dashes_and_X_as_check() { assert!(is_valid_isbn("359821507X")); } #[test] //#[ignore] fn test_invalid_isbn_without_dashes_and_no_check_digit() { assert!(!is_valid_isbn("359821507")); } #[test] //#[ignore] fn test_invalid_isbn_without_dashes_and_too_long() { assert!(!is_valid_isbn("3598215078X")); } #[test] //#[ignore] fn test_invalid_isbn_without_check_digit() { assert!(!is_valid_isbn("3-598-21507")); } #[test] //#[ignore] fn test_invalid_isbn_too_long() { assert!(!is_valid_isbn("3-598-21507-XX")); } #[test] //#[ignore] fn test_valid_digits_invalid_length() { assert!(!is_valid_isbn("35982150881")); } #[test] //#[ignore] fn test_special_characters() { assert!(!is_valid_isbn("!@#%!@")); } #[test] //#[ignore] #[allow(non_snake_case)] fn test_invalid_isbn_with_check_digit_X_instead_of_0() { assert!(!is_valid_isbn("3-598-21515-X")); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { /// An ISBN type #[derive(PartialEq, Eq)] enum IsbnType { Isbn10, Isbn13, } /// Checks if an 'X' is valid at the given position for the given ISBN type #[allow(non_snake_case)] fn is_X_valid(position: &usize, isbn_type: &IsbnType) -> bool { (isbn_type == &IsbnType::Isbn10 && position == &9) || (isbn_type == &IsbnType::Isbn13 && position == &12) } /// Checks if a '-' is valid at the given position for the given ISBN type fn is_dash_valid(position: &usize, isbn_type: &IsbnType) -> bool { isbn_type == &IsbnType::Isbn13 && (position == &1 || position == &5 || position == &11) } /// Determines whether the supplied string is a valid ISBN number pub fn is_valid_isbn(isbn: &str) -> bool { let isbn_type = match isbn.len() { 10 => IsbnType::Isbn10, 13 => IsbnType::Isbn13, _ => return false, }; let mut checksum = 0; let mut coefficient = 10; for (position, c) in isbn.char_indices() { let digit_value = match c { '0'...'9' => c.to_digit(10).unwrap(), 'X' if is_X_valid(&position, &isbn_type) => 10, '-' if is_dash_valid(&position, &isbn_type) => continue, _ => return false, }; checksum += coefficient * digit_value; coefficient -= 1; } checksum % 11 == 0 } #}