IPv6アドレスの確認

Pythonを勉強しようとしているときに、私はあなたが入力を受け取り、それがIPv6基準を満たしているかどうかを確かめることができるコードを書くように頼む質問に出くわした。

私は以下のコードを書きました:

valid_characters = ['A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', ':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
address = input('Please enter an IP address: ')
is_valid = []

for i in range(len(address)):
    current = address[i]
    for j in range(len(valid_characters)):
        check = valid_characters[j]
        if check == current:
            a = 1
            is_valid.append(a)

address_list = address.split(":")
invalid_segment = False

for i in range(len(address_list)):
    current = address_list[i]
    if len(current) > 4:
        invalid_segment = True

if len(address) == len(is_valid) and len(address_list) == 8 and invalid_segment == False:
    print("It is a valid IPv6 address.")

elif invalid_segment:
    print("It is not a valid IPv6 address.")

else:
    print("It is not a valid IPv6 address.")

コードは機能し、提供されたサンプルケースでは、入力されたIPアドレスがIPv6かどうかを確認することに成功していますが、私の解決策はそれほど洗練されていません。

私のコードをどのように圧縮または改善するべきかについて、誰かが私に正しい方向を示すことができれば、私はそれを本当に感謝します。

私はPythonに慣れていない、経験がほとんどないことに注意してください。

15
IPv6アドレスの最後の32ビットを4ドット表記で書くことは正当です。
追加された 著者 user45920,
あなたのコードは :: 1 を拒否し、 1 :: 3 ::: 6 :: 8 を受け入れるようです
追加された 著者 masospaghetti,
あなたのコードをそのまま扱う良い答えがありますが、最善の解決策はあなたが学ぼうとしていることにかかっていることに注意するべきです。それが文字列操作であれば、これは問題ありません。それが効率のために努力するならば、あなたの解決策はおそらくより効率的であろう入力部分のint(、base = 16)を扱うことによってすべての文字列を避けることができます。最終的には、もちろん、現実のソリューションでは、他の回答で示唆されているように、おそらくPythonのipv6クラスを扱うべきです。
追加された 著者 user1561108,

6 答え

Pythonには " Batteries Included "という哲学があります。標準ライブラリの内容を再発明しないでください。

特に、このコードは IPv6Address を利用すればもっと簡単に書くことができます。

import ipaddress

address = input('Please enter an IP address: ')

try:
    addr = ipaddress.IPv6Address(address)
except ipaddress.AddressValueError:
    print(address, 'is not a valid IPv6 address')
else:
    if addr.is_multicast:
        print(address, 'is an IPv6 multicast address')
    if addr.is_private:
        print(address, 'is an IPv6 private address')
    if addr.is_global:
        print(address, 'is an IPv6 global address')
    if addr.is_link_local:
        print(address, 'is an IPv6 link-local address')
    if addr.is_site_local:
        print(address, 'is an IPv6 site-local address')
    if addr.is_reserved:
        print(address, 'is an IPv6 reserved address')
    if addr.is_loopback:
        print(address, 'is an IPv6 loopback address')
    if addr.ipv4_mapped:
        print(address, 'is an IPv6 mapped IPv4 address')
    if addr.sixtofour:
        print(address, 'is an IPv6 RFC 3056 address')
    if addr.teredo:
        print(address, 'is an IPv6 RFC 4380 address')
31
追加された
これは不正行為であり、運動の目的を殺します;-)
追加された 著者 t3chb0t,
これについてのもう一つの素晴らしいところは、それが速記記法にそれを変えることによってあなたのためにIPアドレスを単純化するということです。
追加された 著者 Jenny Pickard,
そしてそれは、埋め込まれたIPv4(例えば、2001:db8:122:344 :: 192.0.2.33 = 2001:db8:122:344 :: c000:221)のような珍しい形式を扱う。
追加された 著者 hirogen,
  1. You can change valid_characters to a string. 'ABCDEFabcdef:0123456789'.
  2. You can use for element in elements: rather than:

    for i in range(len(elements)):
        element = elements[i]
    
  3. You can use in rather than manually perform the check.

    if current in valid_characters:
        is_valid.append(1)
    
  4. You can use a list comprehension, rather than manually build a list.

    is_valid = [
        1
        for current in address
        if current in valid_characters
    ]
    
  5. Rather than manually performing the any check you can use a comprehension to do it for you:

    invalid_segment = any(len(current) > 4 for current in address_list)
    
  6. Rather than checking the length, you can do the same to is_valid:

    is_valid = all(current in valid_characters for current in address)
    
  7. You can invert invalid_segment to simplify the logic later.

  8. You now simplify your checks. You don't need your elif as well.
  9. I'd only use one type of string opener and closer. I personally use '.

これはにつながります:

valid_characters = 'ABCDEFabcdef:0123456789'
address = input('Please enter an IP address: ')

is_valid = all(current in valid_characters for current in address)
address_list = address.split(':')
valid_segment = all(len(current) <= 4 for current in address_list)

if is_valid and valid_segment and len(address_list) == 8:
    print('It is a valid IPv6 address.')
else:
    print('It is not a valid IPv6 address.')

これを機能にすることもできます。そしてそれを使用することができます:

VALID_CHARACTERS = 'ABCDEFabcdef:0123456789'


def valid_ip6(address):
    address_list = address.split(':')
    return (
        len(address_list) == 8
        and all(len(current) <= 4 for current in address_list)
        and all(current in VALID_CHARACTERS for current in address)
    )


if __name__ == '__main__':
    address = input('Please enter an IP address: ')

    if valid_ip6(address):
        print('It is a valid IPv6 address.')
    else:
        print('It is not a valid IPv6 address.')
27
追加された
とてもいい答えです。 Nitpick:OPが早い段階から慣れるように、 name == ' main 'ガードも導入します。
追加された 著者 Thelma,

特定の順序でのさまざまなコメント。


最後の2つのステートメントは同じなので、 invalid_segment をチェックすることは関係ありません。

あなたが書くことができます:

if len(address) == len(is_valid) and len(address_list) == 8 and invalid_segment == False:
    print("It is a valid IPv6 address.")
else:
    print("It is not a valid IPv6 address.")

アドレスをチェックするためにブール値を返す関数を書く方が明確です。コードを理解しやすくし、再利用しやすくします。また、__name__ =の場合、入出力を処理する部分を の後ろに移動することもできます。 = "__main__":ガード

あなたが得るでしょう:

valid_characters = ['A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', ':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

def ipv6_addr_is_valid(address):
    is_valid = []

    for i in range(len(address)):
        current = address[i]
        for j in range(len(valid_characters)):
            check = valid_characters[j]
            if check == current:
                a = 1
                is_valid.append(a)

    address_list = address.split(":")
    invalid_segment = False

    for i in range(len(address_list)):
        current = address_list[i]
        if len(current) > 4:
            invalid_segment = True

    return len(address) == len(is_valid) and len(address_list) == 8 and invalid_segment == False

if __name__ == "__main__":
    address = input('Please enter an IP address: ')
    if ipv6_addr_is_valid(address):
        print("It is a valid IPv6 address.")
    else:
        print("It is not a valid IPv6 address.")

通常、Pythonで配列を反復処理するためにインデックス、 range 、および len を使用する場合、それは間違っています。 Ned Batchelderによるネイティブのようにループするを見ることを強くお勧めします。より簡潔、迅速、簡単にコードを再利用できます。一言で言えば、 for ループは foreach ループとして機能するため、ほとんどの場合、インデックスを処理する必要はまったくありません。

次のようになります。

def ipv6_addr_is_valid(address):
    is_valid = []

    for current in address:
        for valid in valid_characters:
            if valid == current:
                a = 1
                is_valid.append(a)

    address_list = address.split(":")
    invalid_segment = False

    for current in address_list:
        if len(current) > 4:
            invalid_segment = True

    return len(address) == len(is_valid) and len(address_list) == 8 and invalid_segment == False

is_valid リストを使用し、その要素数を使用して住所が有効かどうかを確認します。物事はより効率的な方法で行うことができます。教育目的のために多くの小さなステップでそれをしましょう。

  • you could count values instead of adding them to a list

    nb_valid = 0
    
    for current in address:
        for valid in valid_characters:
            if valid == current:
                nb_valid += 1
    
    ...    
    return len(address) == nb_valid and len(address_list) == 8 and invalid_segment == False
    
  • you could break out of the valid_characters loops as soon as you've found a match.

  • in Python, for loops accept a else part meaning "this loop exited the normal way: it didn't encounter a break". You could use this to return False as soon as a character is invalid.

    for current in address: for valid in valid_characters: if valid == current: nb_valid += 1 break else: # nobreak - invalid character return False

したがって、もう nb_valid を数える必要はありません。

for current in address:
    for valid in valid_characters:
        if valid == current:
            break
    else:  # nobreak - invalid character
        return False
  • You could use in to check if character is valid:

    for current in address: if current not in valid_characters: return False

  • You could initialise valid_characters in a more concise way in a single string:

    valid_characters = 'ABCDEFabcdef:0123456789'

次に、 set を使用して in 検索を効率的に行うことができます。

valid_characters = set('ABCDEFabcdef:0123456789')

無効なセグメントを見つけたら break できます。直接Falseを返して変数を削除することもできます。

for current in address_list:
    if len(current) > 4:
        return False  # Invalid segment

この段階では、コードは次のようになります。

valid_characters = set('ABCDEFabcdef:0123456789')

def ipv6_addr_is_valid(address):
    for current in address:
        if current not in valid_characters:
            return False


    address_list = address.split(":")

    for current in address_list:
        if len(current) > 4:
            return False  # Invalid segment

    return len(address_list) == 8

if __name__ == "__main__":
    print(ipv6_addr_is_valid("0:0:0:0:0:0:0:1"))
    # address = input('Please enter an IP address: ')
    # if ipv6_addr_is_valid(address):
    #     print("It is a valid IPv6 address.")
    # else:
    #     print("It is not a valid IPv6 address.")

コードの一部をより簡潔にするために、 all / any を使用できます。あなたは既に return を使っているので、あまり役に立ちません。

def ipv6_addr_is_valid(address):
    if any(c not in valid_characters for c in address):
        return False
    address_list = address.split(":")

    if any(len(c) > 4 for c in address_list):
        return False  # Invalid segment

    return len(address_list) == 8

再編成することができます:

def ipv6_addr_is_valid(address):
    address_list = address.split(":")
    return len(address_list) == 8 and all(c in valid_characters for c in address) and all(len(c) <= 4 for c in address_list)
10
追加された
@Peilonrayz OPがPythonの初心者であると言ったので、少しずつ変更を加え、さまざまな手法を示したいと思いました。
追加された 著者 Mike,
anyall を使用する最悪の方法を選択するのはなぜですか。 adress_list を上に移動して、代わりに all を使用する場合は1つの戻り値を使用できます。
追加された 著者 Taamer,

IPv6アドレスに関するウィキペディアの記事によると、IPv6アドレスを表す別の方法がありますあなたがこのアプローチを考慮に入れていないこと:

グループ内の先行ゼロは省略することができますが、各グループは少なくとも1つの16進数を保持する必要があります。

...

2つの連続したコロン(::)を使用して、ゼロのみを含む1つ以上の連続したグループを単一の空のグループに置き換えることができます。

...

ローカルホスト(ループバック)アドレス0:0:0:0:0:0:0:1、およびIPv6未指定アドレス0:0:0:0:0:0:0:0:0が削減されます。それぞれ:: 1および::に。

8
追加された

ホイールを作り直さないで、 netaddr モジュールを使用してください。 IPが有効か無効かに基づいて、 True / False が返されます。

import netaddr

def validate_v6_ip(ip_str):
    """Function for validating IPv6 addresses.

        validate_v6_ip()
    """
    assert type(ip_str) == str, 'Input must be a string.'
    return netaddr.valid_ipv6(ip_str.strip())
6
追加された

IPv6アドレスを正しく検証するのは難しいです。あなたのコードは正しくそれをしません。有効なケースを拒否し、無効なケースを受け入れます。

自分でやらなければならないとしたら、次のようにします。

  • 「:」で文字列を分割します。
  • 住所の最後の部分に「ドット付き10進数の4進数」がないか確認します。見つかった場合は、最後の部分を2つの16進数部分に置き換えます。
  • 次に棄権を確認します。省略形は、先頭(例::: 1)、末尾(例:2001::)、途中のどこか(例:2001 :: 1)、またはアドレス内の唯一のもの(:::)として使用できます。あなたはこれらのすべてのケースを偽の省略形を許可せずに扱う必要があります。
  • 住所の部分数が許容範囲内であることを確認します。許容される部品数の範囲は、住所が省略されているかどうかによって異なります。
  • 最後に個々の部分を検証します。

しかし、これが問題にならない限り、言われてきたように、それを図書館にパントするほうがたぶん良いでしょう。

3
追加された