使用 Python 从文本中提取 IBAN
Extract IBAN from text with Python
我想从 Python 的文本中提取 IBAN 号码。这里的挑战是,IBAN 本身可以用数字之间的空格以多种方式编写,我发现很难将其转换为有用的正则表达式模式。
我写了一个 demo version 试图从文本中匹配所有德国和奥地利的 IBAN 号码。
^DE([0-9a-zA-Z]\s?){20}$
我在Whosebug上看到过类似的问题。但是,结合使用不同的方式来编写 IBAN 号码以及从文本中提取这些号码,这让我的问题很难解决。
希望你能帮助我!
一般来说,要匹配德国和奥地利的 IBAN 代码,您可以使用
codes = re.findall(r'\b(DE(?:\s*[0-9]){20}|AT(?:\s*[0-9]){18})\b(?!\s*[0-9])', text)
详情:
\b
- 单词边界
(DE(?:\s*[0-9]){20}|AT(?:\s*[0-9]){18})
- 第 1 组:DE
和 20 次重复的数字,中间有任意数量的空格,或者 AT
然后 18 次重复的单个数字,最终用任何空格分隔空格数量
\b(?!\s*[0-9])
- 没有紧跟零个或多个空格和一个 ASCII 数字的字边界。
参见 this regex demo。
对于您在问题中显示的包含非正确 IBAN 代码的数据,您可以使用
\b(?:DE|AT)(?:\s?[0-9a-zA-Z]){18}(?:(?:\s?[0-9a-zA-Z]){2})?\b
见regex demo。 详情:
\b
- 单词边界
(?:DE|AT)
- DE
或 AT
(?:\s?[0-9a-zA-Z]){18}
- 出现 18 次可选空格,然后是字母数字字符
(?:(?:\s?[0-9a-zA-Z]){2})?
- 可选空格和字母数字字符的两个序列的可选出现
\b
- 单词边界。
ISO landcode
Verification#
Bank#
Account#
Germany
2a
2n
8n
10n
Austria
2a
2n
5n
11n
注意: a - 字母(仅限字母),n - 数字(仅限数字)
所以主要区别实际上是数字长度。这意味着您可以尝试:
\b(?:DE(?:\s*\d){20}|AT(?:\s*\d){18})\b(?!\s*\d)
网上看demo.
\b
- 字边界。
(?:
- 打开第一个非捕获组。
DE
- 按字面匹配大写“DE”。
(?:
- 打开第二个非捕获组。
\s*\d
- 零个或多个空格,最多一位数。
){20}
- 关闭第二个非捕获组并匹配它 20 次。
|
- 或者:
AT
- 按字面匹配大写“AT”。
(?:
- 打开第三个非捕获组。
\s*\d
- 零个或多个空格,最多一位数。
){18}
- 关闭第二个非捕获组并匹配它 20 次。
)
- 关闭第一个非捕获组。
\b
- 字边界。
(?!\s*\d)
- 否定前瞻以防止任何尾随数字。
确实表明您的奥地利 IBAN 号码无效。如果您想提取到它们仍然有效的程度,我想您可以删除 \b(?!\s*\d)
假设您在 class 中使用此验证并将 self.input 作为输入字符串,请使用以下代码。不过,如果您只想验证德国和奥地利的 IBAN,我建议您从字典中删除所有其他国家/地区:
country_dic = {
"AL": [28, "Albania"],
"AD": [24, "Andorra"],
"AT": [20, "Austria"],
"BE": [16, "Belgium"],
"BA": [20, "Bosnia"],
"BG": [22, "Bulgaria"],
"HR": [21, "Croatia"],
"CY": [28, "Cyprus"],
"CZ": [24, "Czech Republic"],
"DK": [18, "Denmark"],
"EE": [20, "Estonia"],
"FO": [18, "Faroe Islands"],
"FI": [18, "Finland"],
"FR": [27, "France"],
"DE": [22, "Germany"],
"GI": [23, "Gibraltar"],
"GR": [27, "Greece"],
"GL": [18, "Greenland"],
"HU": [28, "Hungary"],
"IS": [26, "Iceland"],
"IE": [22, "Ireland"],
"IL": [23, "Israel"],
"IT": [27, "Italy"],
"LV": [21, "Latvia"],
"LI": [21, "Liechtenstein"],
"LT": [20, "Lithuania"],
"LU": [20, "Luxembourg"],
"MK": [19, "Macedonia"],
"MT": [31, "Malta"],
"MU": [30, "Mauritius"],
"MC": [27, "Monaco"],
"ME": [22, "Montenegro"],
"NL": [18, "Netherlands"],
"NO": [15, "Northern Ireland"],
"PO": [28, "Poland"],
"PT": [25, "Portugal"],
"RO": [24, "Romania"],
"SM": [27, "San Marino"],
"SA": [24, "Saudi Arabia"],
"RS": [22, "Serbia"],
"SK": [24, "Slovakia"],
"SI": [19, "Slovenia"],
"ES": [24, "Spain"],
"SE": [24, "Sweden"],
"CH": [21, "Switzerland"],
"TR": [26, "Turkey"],
"TN": [24, "Tunisia"],
"GB": [22, "United Kingdom"]
} # dictionary with IBAN-length per country-code
def eval_iban(self):
# Evaluates how many IBAN's are found in the input string
try:
if self.input:
hits = 0
for word in self.input.upper().split():
iban = word.strip()
letter_dic = {ord(d): str(i) for i, d in enumerate(
string.digits + string.ascii_uppercase)} # Matches letter to number for 97-proof method
correct_length = country_dic[iban[:2]]
if len(iban) == correct_length[0]: # checks whether country-code matches IBAN-length
if int((iban[4:] + iban[:4]).translate(letter_dic)) % 97 == 1:
# checks whether converted letters to numbers result in 1 when divided by 97
# this validates the IBAN
hits += 1
return hits
return 0
except KeyError:
return 0
except Exception:
# logging.exception('Could not evaluate IBAN')
return 0
我想从 Python 的文本中提取 IBAN 号码。这里的挑战是,IBAN 本身可以用数字之间的空格以多种方式编写,我发现很难将其转换为有用的正则表达式模式。
我写了一个 demo version 试图从文本中匹配所有德国和奥地利的 IBAN 号码。
^DE([0-9a-zA-Z]\s?){20}$
我在Whosebug上看到过类似的问题。但是,结合使用不同的方式来编写 IBAN 号码以及从文本中提取这些号码,这让我的问题很难解决。
希望你能帮助我!
一般来说,要匹配德国和奥地利的 IBAN 代码,您可以使用
codes = re.findall(r'\b(DE(?:\s*[0-9]){20}|AT(?:\s*[0-9]){18})\b(?!\s*[0-9])', text)
详情:
\b
- 单词边界(DE(?:\s*[0-9]){20}|AT(?:\s*[0-9]){18})
- 第 1 组:DE
和 20 次重复的数字,中间有任意数量的空格,或者AT
然后 18 次重复的单个数字,最终用任何空格分隔空格数量\b(?!\s*[0-9])
- 没有紧跟零个或多个空格和一个 ASCII 数字的字边界。
参见 this regex demo。
对于您在问题中显示的包含非正确 IBAN 代码的数据,您可以使用
\b(?:DE|AT)(?:\s?[0-9a-zA-Z]){18}(?:(?:\s?[0-9a-zA-Z]){2})?\b
见regex demo。 详情:
\b
- 单词边界(?:DE|AT)
-DE
或AT
(?:\s?[0-9a-zA-Z]){18}
- 出现 18 次可选空格,然后是字母数字字符(?:(?:\s?[0-9a-zA-Z]){2})?
- 可选空格和字母数字字符的两个序列的可选出现\b
- 单词边界。
ISO landcode | Verification# | Bank# | Account# | |
---|---|---|---|---|
Germany | 2a | 2n | 8n | 10n |
Austria | 2a | 2n | 5n | 11n |
注意: a - 字母(仅限字母),n - 数字(仅限数字)
所以主要区别实际上是数字长度。这意味着您可以尝试:
\b(?:DE(?:\s*\d){20}|AT(?:\s*\d){18})\b(?!\s*\d)
网上看demo.
\b
- 字边界。(?:
- 打开第一个非捕获组。DE
- 按字面匹配大写“DE”。(?:
- 打开第二个非捕获组。\s*\d
- 零个或多个空格,最多一位数。){20}
- 关闭第二个非捕获组并匹配它 20 次。
|
- 或者:AT
- 按字面匹配大写“AT”。(?:
- 打开第三个非捕获组。\s*\d
- 零个或多个空格,最多一位数。){18}
- 关闭第二个非捕获组并匹配它 20 次。
)
- 关闭第一个非捕获组。
\b
- 字边界。(?!\s*\d)
- 否定前瞻以防止任何尾随数字。
确实表明您的奥地利 IBAN 号码无效。如果您想提取到它们仍然有效的程度,我想您可以删除 \b(?!\s*\d)
假设您在 class 中使用此验证并将 self.input 作为输入字符串,请使用以下代码。不过,如果您只想验证德国和奥地利的 IBAN,我建议您从字典中删除所有其他国家/地区:
country_dic = {
"AL": [28, "Albania"],
"AD": [24, "Andorra"],
"AT": [20, "Austria"],
"BE": [16, "Belgium"],
"BA": [20, "Bosnia"],
"BG": [22, "Bulgaria"],
"HR": [21, "Croatia"],
"CY": [28, "Cyprus"],
"CZ": [24, "Czech Republic"],
"DK": [18, "Denmark"],
"EE": [20, "Estonia"],
"FO": [18, "Faroe Islands"],
"FI": [18, "Finland"],
"FR": [27, "France"],
"DE": [22, "Germany"],
"GI": [23, "Gibraltar"],
"GR": [27, "Greece"],
"GL": [18, "Greenland"],
"HU": [28, "Hungary"],
"IS": [26, "Iceland"],
"IE": [22, "Ireland"],
"IL": [23, "Israel"],
"IT": [27, "Italy"],
"LV": [21, "Latvia"],
"LI": [21, "Liechtenstein"],
"LT": [20, "Lithuania"],
"LU": [20, "Luxembourg"],
"MK": [19, "Macedonia"],
"MT": [31, "Malta"],
"MU": [30, "Mauritius"],
"MC": [27, "Monaco"],
"ME": [22, "Montenegro"],
"NL": [18, "Netherlands"],
"NO": [15, "Northern Ireland"],
"PO": [28, "Poland"],
"PT": [25, "Portugal"],
"RO": [24, "Romania"],
"SM": [27, "San Marino"],
"SA": [24, "Saudi Arabia"],
"RS": [22, "Serbia"],
"SK": [24, "Slovakia"],
"SI": [19, "Slovenia"],
"ES": [24, "Spain"],
"SE": [24, "Sweden"],
"CH": [21, "Switzerland"],
"TR": [26, "Turkey"],
"TN": [24, "Tunisia"],
"GB": [22, "United Kingdom"]
} # dictionary with IBAN-length per country-code
def eval_iban(self):
# Evaluates how many IBAN's are found in the input string
try:
if self.input:
hits = 0
for word in self.input.upper().split():
iban = word.strip()
letter_dic = {ord(d): str(i) for i, d in enumerate(
string.digits + string.ascii_uppercase)} # Matches letter to number for 97-proof method
correct_length = country_dic[iban[:2]]
if len(iban) == correct_length[0]: # checks whether country-code matches IBAN-length
if int((iban[4:] + iban[:4]).translate(letter_dic)) % 97 == 1:
# checks whether converted letters to numbers result in 1 when divided by 97
# this validates the IBAN
hits += 1
return hits
return 0
except KeyError:
return 0
except Exception:
# logging.exception('Could not evaluate IBAN')
return 0