การแปลง int เป็นไบต์ใน Python 3


177

ฉันพยายามสร้างวัตถุไบต์นี้ใน Python 3:

b'3\r\n'

ดังนั้นฉันพยายามชัดเจน (สำหรับฉัน) และพบพฤติกรรมแปลก:

>>> bytes(3) + b'\r\n'
b'\x00\x00\x00\r\n'

เห็นได้ชัดว่า:

>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

ฉันไม่เห็นพอยน์เตอร์ใด ๆ ว่าทำไมการแปลงไบต์จึงใช้วิธีนี้ในการอ่านเอกสาร อย่างไรก็ตามฉันพบข้อความแปลกใจบางอย่างในเรื่อง Python เกี่ยวกับการเพิ่มformatลงในไบต์ (ดูรูปแบบ Python 3 ไบต์ ):

http://bugs.python.org/issue3982

สิ่งนี้โต้ตอบได้ไม่ดียิ่งขึ้นกับสิ่งแปลกประหลาดเช่นไบต์ (int) ที่คืนค่าศูนย์ในขณะนี้

และ:

มันจะสะดวกกว่าสำหรับฉันถ้า bytes (int) คืนค่า ASCIIfication ของ int นั้น แต่โดยสุจริตแม้ข้อผิดพลาดจะดีกว่าพฤติกรรมนี้ (ถ้าฉันต้องการพฤติกรรมนี้ - ซึ่งฉันไม่เคยมี - ฉันอยากให้มันเป็นวิธีการในชั้นเรียนที่เรียกเช่น "bytes.zeroes (n)")

บางคนสามารถอธิบายได้ว่าพฤติกรรมนี้มาจากไหน


1
เกี่ยวข้องกับชื่อเรื่อง:3 .to_bytes
jfs

2
มันไม่ชัดเจนจากคำถามของคุณหากคุณต้องการค่าจำนวนเต็ม 3 หรือค่าของอักขระ ASCII ที่แสดงถึงหมายเลขสาม (ค่าจำนวนเต็ม 51) แรกคือไบต์ ([3]) == b '\ x03' หลังคือไบต์ ([ord ('3')]) == b'3 '
florisla

คำตอบ:


177

นั่นคือวิธีที่มันได้รับการออกแบบ - และมันสมเหตุสมผลเพราะโดยปกติแล้วคุณจะต้องเรียกมันbytesซ้ำได้แทนที่จะเป็นจำนวนเต็มเดียว:

>>> bytes([3])
b'\x03'

เอกสารรัฐนี้เช่นเดียวกับ docstring สำหรับbytes:

 >>> help(bytes)
 ...
 bytes(int) -> bytes object of size given by the parameter initialized with null bytes

25
ระวังว่าผลงานดังกล่าวข้างต้นเท่านั้นที่มีงูหลาม 3. ในหลาม 2 bytesเป็นเพียงนามแฝงสำหรับstrซึ่งหมายความว่าจะช่วยให้คุณbytes([3]) '[3]'
botchniaque

8
ในหลาม 3 ทราบว่าbytes([n])จะทำงานเฉพาะสำหรับ int n จาก 0 ถึง 255 ValueErrorสำหรับสิ่งอื่นมันก็เกิด
คิวเมนตัส

8
@ABB: ไม่น่าแปลกใจจริงๆเนื่องจากไบต์สามารถเก็บค่าได้ระหว่าง 0 ถึง 255 เท่านั้น
Tim Pietzcker

7
ควรสังเกตว่าbytes([3])ยังคงแตกต่างจากที่ OP ต้องการ - คือค่าไบต์ที่ใช้ในการเข้ารหัสตัวเลข "3" ใน ASCII เช่น bytes([51])ซึ่งเป็นไม่ได้b'3' b'\x03'
lenz

2
bytes(500)สร้าง bytestring w / len == 500 มันไม่ได้สร้าง bytestring ที่เข้ารหัสจำนวนเต็ม 500 และฉันยอมรับว่าbytes([500])ไม่สามารถใช้งานได้ซึ่งเป็นสาเหตุที่เป็นคำตอบที่ผิดเช่นกัน อาจเป็นคำตอบที่ถูกต้องint.to_bytes()สำหรับรุ่น> = 3.1
weberc2

199

จาก python 3.2 คุณสามารถทำได้

>>> (1024).to_bytes(2, byteorder='big')
b'\x04\x00'

https://docs.python.org/3/library/stdtypes.html#int.to_bytes

def int_to_bytes(x: int) -> bytes:
    return x.to_bytes((x.bit_length() + 7) // 8, 'big')

def int_from_bytes(xbytes: bytes) -> int:
    return int.from_bytes(xbytes, 'big')

ดังนั้น, x == int_from_bytes(int_to_bytes(x)). โปรดทราบว่าการเข้ารหัสนี้ใช้ได้เฉพาะกับจำนวนเต็มที่ไม่ได้ลงนาม (ไม่ใช่ลบ)


4
ในขณะที่คำตอบนี้ดี แต่ใช้ได้เฉพาะกับจำนวนเต็ม (ไม่ใช่ลบ) ที่ไม่ได้ลงนาม ฉันได้ดัดแปลงมันเขียนคำตอบซึ่งยังใช้งานได้สำหรับจำนวนเต็มลงนาม
คิวเมนตัส

1
นั่นไม่ได้ช่วยในการรับb"3"จาก3ตามคำถามที่ถาม (มันจะให้b"\x03".)
gsnedders

40

คุณสามารถใช้ชุดของ struct :

In [11]: struct.pack(">I", 1)
Out[11]: '\x00\x00\x00\x01'

ว่า ">" คือสั่งไบต์ (big-)และ "ฉัน" เป็นตัวอักษรรูปแบบ ดังนั้นคุณสามารถเจาะจงได้ถ้าคุณต้องการทำอย่างอื่น:

In [12]: struct.pack("<H", 1)
Out[12]: '\x01\x00'

In [13]: struct.pack("B", 1)
Out[13]: '\x01'

นี้ทำงานเหมือนกันในทั้ง 2 หลามและงูหลาม 3

หมายเหตุ: การดำเนินการผกผัน (ไบต์ int) สามารถทำได้ด้วยการแกะ


2
@AndyHayden ชี้แจงตั้งแต่ struct มีขนาดมาตรฐานโดยไม่คำนึงถึงการป้อนข้อมูลI, HและBการทำงานจนถึง2**k - 1ที่ k เป็น 32, 16, และ 8 ตามลำดับ struct.errorสำหรับปัจจัยการผลิตขนาดใหญ่ที่พวกเขายก
คิวเมนตัส

ลงคะแนนอย่างน่าจะเป็นเพราะมันไม่ได้ตอบคำถาม: OP ต้องการทราบวิธีการสร้างb'3\r\n'เช่นไบต์สตริงที่มีอักขระ ASCII "3" ไม่ใช่อักขระ ASCII "\ x03"
Dave Jones

1
@DaveJones อะไรที่ทำให้คุณคิดว่านั่นเป็นสิ่งที่ OP ต้องการ? ยอมรับคำตอบผลตอบแทน\x03และวิธีการแก้ปัญหาหากคุณต้องการเพียงแค่b'3'จิ๊บจ๊อย เหตุผลที่อ้างโดย ABB มีเหตุผลมากขึ้น ... หรืออย่างน้อยก็เข้าใจได้
Andy Hayden

@DaveJones ด้วยเหตุผลที่ฉันเพิ่มคำตอบนี้เป็นเพราะ Google พาคุณไปที่นี่เมื่อค้นหาเพื่อทำสิ่งนี้อย่างแม่นยำ นั่นคือเหตุผลที่มาถึงที่นี่
Andy Hayden

4
ไม่เพียงทำงานใน 2 และ 3 เท่านั้น แต่เร็วกว่าทั้งสองbytes([x])และ(x).to_bytes()วิธีการใน Python 3.5 นั่นเป็นสิ่งที่ไม่คาดคิด
Mark Ransom

25

Python 3.5+ แนะนำ% -interpolation ( printf- การจัดรูปแบบสไตล์) สำหรับไบต์ :

>>> b'%d\r\n' % 3
b'3\r\n'

ดูPEP 0461 - การเพิ่มการจัดรูปแบบ% เป็นไบต์และ bytearrayByteArray

ในเวอร์ชันก่อนหน้าคุณสามารถใช้strและ.encode('ascii')ผลลัพธ์:

>>> s = '%d\r\n' % 3
>>> s.encode('ascii')
b'3\r\n'

หมายเหตุ: มันแตกต่างจากสิ่งที่int.to_bytesผลิต :

>>> n = 3
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x03'
>>> b'3' == b'\x33' != '\x03'
True

11

เอกสารกล่าวว่า:

bytes(int) -> bytes object of size given by the parameter
              initialized with null bytes

ลำดับ:

b'3\r\n'

มันเป็นตัวละคร '3' (ทศนิยม 51) ตัวละคร '\ r' (13) และ '\ n' (10)

ดังนั้นวิธีที่จะปฏิบัติต่อมันเช่น:

>>> bytes([51, 13, 10])
b'3\r\n'

>>> bytes('3', 'utf8') + b'\r\n'
b'3\r\n'

>>> n = 3
>>> bytes(str(n), 'ascii') + b'\r\n'
b'3\r\n'

ทดสอบกับ IPython 1.1.0 และ Python 3.2.3


1
ฉันสิ้นสุดการทำหรือbytes(str(n), 'ascii') + b'\r\n' str(n).encode('ascii') + b'\r\n'ขอบคุณ! :)
astrojuanlu

1
@ Juanlu001 "{}\r\n".format(n).encode()ฉันไม่คิดว่าจะมีอันตรายใด ๆ เกิดขึ้นโดยใช้การเข้ารหัส utf8 ที่เป็นค่าเริ่มต้น
John La Rooy

6

ASCIIfication ของ 3 "\x33"ไม่ใช่"\x03"!

นั่นคือสิ่งที่หลามทำเพื่อstr(3)แต่มันจะผิดโดยสิ้นเชิงสำหรับไบต์ตามที่ควรพิจารณาอาร์เรย์ของข้อมูลไบนารีและไม่ถูกใช้เป็นสตริง

วิธีที่ง่ายที่สุดในการบรรลุสิ่งที่คุณต้องการคือbytes((3,))ดีกว่าbytes([3])เพราะการเริ่มต้นรายการมีราคาแพงกว่ามากดังนั้นอย่าใช้รายการเมื่อคุณสามารถใช้สิ่งอันดับ คุณสามารถแปลงจำนวนเต็มที่ใหญ่กว่าโดยใช้int.to_bytes(3, "little")คุณสามารถแปลงจำนวนเต็มขนาดใหญ่โดยใช้

การกำหนดค่าเริ่มต้นไบต์ด้วยความยาวที่กำหนดทำให้สมเหตุสมผลและมีประโยชน์มากที่สุดเนื่องจากมักใช้เพื่อสร้างบัฟเฟอร์บางประเภทซึ่งคุณต้องการหน่วยความจำบางส่วนตามขนาดที่กำหนด ฉันมักจะใช้สิ่งนี้เมื่อเริ่มต้นอาร์เรย์หรือขยายไฟล์บางส่วนโดยการเขียนเลขศูนย์ไป


1
(ก) การหลบหนีของสัญกรณ์: มีหลายปัญหาที่เกิดขึ้นกับคำตอบนี้b'3'เป็นไม่ได้b'\x33' b'\x32'(b) (3)ไม่ใช่ tuple - คุณต้องเพิ่มเครื่องหมายจุลภาค (c) สถานการณ์ของการเริ่มต้นลำดับด้วย zeroes ไม่ได้ใช้กับbytesวัตถุเนื่องจากมันไม่เปลี่ยนรูป (มันสมเหตุสมผลสำหรับbytearrays)
lenz

ขอบคุณสำหรับความคิดเห็นของคุณ ฉันแก้ไขข้อผิดพลาดที่ชัดเจนทั้งสองนั้น ในกรณีของbytesและbytearrayฉันคิดว่ามันเป็นเรื่องของความมั่นคง แต่มันก็มีประโยชน์ถ้าคุณต้องการที่จะผลักค่าศูนย์ลงในบัฟเฟอร์หรือไฟล์ซึ่งในกรณีนี้มันถูกใช้เป็นแหล่งข้อมูลเท่านั้น
Bachsau

5

int(รวมถึง Python2 long) สามารถแปลงเป็นbytesฟังก์ชั่นต่อไปนี้:

import codecs

def int2bytes(i):
    hex_value = '{0:x}'.format(i)
    # make length of hex_value a multiple of two
    hex_value = '0' * (len(hex_value) % 2) + hex_value
    return codecs.decode(hex_value, 'hex_codec')

การแปลงกลับสามารถทำได้โดยอีกอันหนึ่ง:

import codecs
import six  # should be installed via 'pip install six'

long = six.integer_types[-1]

def bytes2int(b):
    return long(codecs.encode(b, 'hex_codec'), 16)

ทั้งสองฟังก์ชั่นใช้งานได้ทั้ง Python2 และ Python3


'hex_value ='% x '% i' จะไม่ทำงานภายใต้ Python 3.4 คุณได้รับ TypeError ดังนั้นคุณต้องใช้ hex () แทน
bjmc

@bjmc ถูกแทนที่ด้วย str.format สิ่งนี้ควรใช้กับ Python 2.6+
renskiy

ขอบคุณ @renskiy คุณอาจต้องการใช้ 'hex_codec' แทน 'hex' เพราะดูเหมือนว่านามแฝง 'hex' จะไม่สามารถใช้ได้กับทุกรุ่น Python 3 ดูstackoverflow.com/a/12917604/845210
bjmc

@bjmc แก้ไขแล้ว ขอบคุณ
renskiy

สิ่งนี้ล้มเหลวในจำนวนเต็มลบใน python 3.6
Berserker

4

ฉันอยากรู้เกี่ยวกับประสิทธิภาพของวิธีการต่าง ๆ สำหรับ int เดี่ยวในช่วง[0, 255]ดังนั้นฉันจึงตัดสินใจทำการทดสอบเวลา

ขึ้นอยู่กับการกำหนดเวลาดังต่อไปนี้และจากแนวโน้มทั่วไปผมสังเกตจากการพยายามค่าที่แตกต่างกันและการกำหนดค่าstruct.packน่าจะเป็นที่เร็วที่สุดตามด้วยint.to_bytes, bytesและด้วยstr.encode(แปลกใจ) เป็นที่ช้าที่สุด โปรดทราบว่าผลลัพธ์แสดงความหลากหลายมากกว่าที่แสดงไว้int.to_bytesและbytesบางครั้งก็สลับการจัดอันดับความเร็วในระหว่างการทดสอบ แต่struct.packเห็นได้ชัดว่าเร็วที่สุด

ผลลัพธ์ใน CPython 3.7 บน Windows:

Testing with 63:
bytes_: 100000 loops, best of 5: 3.3 usec per loop
to_bytes: 100000 loops, best of 5: 2.72 usec per loop
struct_pack: 100000 loops, best of 5: 2.32 usec per loop
chr_encode: 50000 loops, best of 5: 3.66 usec per loop

โมดูลทดสอบ (ชื่อint_to_byte.py):

"""Functions for converting a single int to a bytes object with that int's value."""

import random
import shlex
import struct
import timeit

def bytes_(i):
    """From Tim Pietzcker's answer:
    https://stackoverflow.com/a/21017834/8117067
    """
    return bytes([i])

def to_bytes(i):
    """From brunsgaard's answer:
    https://stackoverflow.com/a/30375198/8117067
    """
    return i.to_bytes(1, byteorder='big')

def struct_pack(i):
    """From Andy Hayden's answer:
    https://stackoverflow.com/a/26920966/8117067
    """
    return struct.pack('B', i)

# Originally, jfs's answer was considered for testing,
# but the result is not identical to the other methods
# https://stackoverflow.com/a/31761722/8117067

def chr_encode(i):
    """Another method, from Quuxplusone's answer here:
    https://codereview.stackexchange.com/a/210789/140921

    Similar to g10guang's answer:
    https://stackoverflow.com/a/51558790/8117067
    """
    return chr(i).encode('latin1')

converters = [bytes_, to_bytes, struct_pack, chr_encode]

def one_byte_equality_test():
    """Test that results are identical for ints in the range [0, 255]."""
    for i in range(256):
        results = [c(i) for c in converters]
        # Test that all results are equal
        start = results[0]
        if any(start != b for b in results):
            raise ValueError(results)

def timing_tests(value=None):
    """Test each of the functions with a random int."""
    if value is None:
        # random.randint takes more time than int to byte conversion
        # so it can't be a part of the timeit call
        value = random.randint(0, 255)
    print(f'Testing with {value}:')
    for c in converters:
        print(f'{c.__name__}: ', end='')
        # Uses technique borrowed from https://stackoverflow.com/q/19062202/8117067
        timeit.main(args=shlex.split(
            f"-s 'from int_to_byte import {c.__name__}; value = {value}' " +
            f"'{c.__name__}(value)'"
        ))

1
@ABB เป็นที่กล่าวถึงในประโยคแรกของฉันฉันก็แค่วัดนี้สำหรับ int [0, 255]เดียวในช่วง ฉันถือว่าด้วย "ตัวบ่งชี้ที่ไม่ถูกต้อง" คุณหมายถึงการวัดของฉันไม่ได้เป็นทั่วไปเพียงพอที่จะเหมาะสมกับสถานการณ์ส่วนใหญ่ใช่หรือไม่ หรือวิธีการวัดของฉันไม่ดี? หากหลังฉันสนใจที่จะฟังสิ่งที่คุณพูด แต่ถ้าในอดีตฉันไม่เคยอ้างว่าการวัดของฉันเป็นเรื่องทั่วไปสำหรับทุกกรณี สำหรับสถานการณ์ (อาจเฉพาะ) ของฉันฉันเพียงจัดการกับ ints ในช่วง[0, 255]และนั่นคือผู้ชมที่ฉันตั้งใจจะอยู่กับคำตอบนี้ คำตอบของฉันไม่ชัดเจน? ฉันสามารถแก้ไขเพื่อความชัดเจน ...
เกรแฮม

1
เทคนิคการทำดัชนีการเข้ารหัสที่คำนวณล่วงหน้าสำหรับช่วงนั้นเป็นอย่างไร การประมวลผลล่วงหน้าจะไม่ขึ้นอยู่กับจังหวะเวลาเท่านั้น
คิวเมนตัส

@ABB นั่นเป็นความคิดที่ดี ฟังดูเหมือนว่ามันจะเร็วกว่าอย่างอื่น ฉันจะทำเวลาและเพิ่มไปยังคำตอบนี้เมื่อฉันมีเวลา
เกรแฮม

3
หากคุณต้องการกำหนดเวลาเป็นไบต์จากการทำซ้ำคุณควรใช้ bytes((i,))แทนbytes([i])เนื่องจากรายการมีความซับซ้อนมากขึ้นใช้หน่วยความจำมากขึ้นและใช้เวลานานในการเริ่มต้น ในกรณีนี้เพื่ออะไร
Bachsau

4

แม้ว่าคำตอบก่อนหน้าโดย brunsgaardเป็นการเข้ารหัสที่มีประสิทธิภาพ แต่จะทำงานเฉพาะกับจำนวนเต็มที่ไม่ได้ลงนามเท่านั้น สิ่งนี้สร้างขึ้นเพื่อใช้กับทั้งจำนวนเต็มที่ลงชื่อและไม่ได้ลงชื่อ

def int_to_bytes(i: int, *, signed: bool = False) -> bytes:
    length = ((i + ((i * signed) < 0)).bit_length() + 7 + signed) // 8
    return i.to_bytes(length, byteorder='big', signed=signed)

def bytes_to_int(b: bytes, *, signed: bool = False) -> int:
    return int.from_bytes(b, byteorder='big', signed=signed)

# Test unsigned:
for i in range(1025):
    assert i == bytes_to_int(int_to_bytes(i))

# Test signed:
for i in range(-1024, 1025):
    assert i == bytes_to_int(int_to_bytes(i, signed=True), signed=True)

สำหรับตัวเข้ารหัส(i + ((i * signed) < 0)).bit_length()นั้นใช้แทนi.bit_length()เพราะตัวหลังนำไปสู่การเข้ารหัสที่ไม่มีประสิทธิภาพของ -128, -32768 เป็นต้น


เครดิต: CervEd สำหรับแก้ไขความไร้ประสิทธิภาพเล็กน้อย


int_to_bytes(-128, signed=True) == (-128).to_bytes(1, byteorder="big", signed=True)คือFalse
CervEd

คุณไม่ได้ใช้ความยาว 2 คุณกำลังคำนวณความยาวบิตของจำนวนเต็มที่ลงนามแล้วบวก 7 จากนั้น 1 ถ้าเป็นจำนวนเต็มที่ลงชื่อ ในที่สุดคุณก็แปลงความยาวเป็นไบต์ นี้ทำให้ผลที่ไม่คาดคิดสำหรับ-128, -32768ฯลฯ
CervEd

ขอให้เรายังคงอภิปรายนี้ในการแชท
CervEd

นี้เป็นวิธีที่คุณแก้ไขได้(i+(signed*i<0)).bit_length()
CervEd

3

พฤติกรรมนี้ได้มาจากความจริงที่ว่าในหลามก่อนที่จะมี 3 รุ่นเป็นเพียงนามแฝงสำหรับbytes strใน Python3.x bytesเป็นเวอร์ชันที่ไม่เปลี่ยนรูปแบบของbytearray- ชนิดใหม่ที่สมบูรณ์ไม่เข้ากันได้ย้อนหลัง


3

จากเอกสารจำนวนไบต์ :

ดังนั้นข้อโต้แย้งคอนสตรัคถูกตีความว่าเป็น bytearray ()

จากนั้นโดยใช้เอกสารโดย :

พารามิเตอร์ต้นทางที่เป็นทางเลือกสามารถใช้ในการเริ่มต้นอาร์เรย์ได้หลายวิธี:

  • ถ้าเป็นจำนวนเต็มอาร์เรย์จะมีขนาดนั้นและจะถูกเตรียมใช้งานด้วยไบต์ว่าง

หมายเหตุที่แตกต่างจาก 2.x (ที่ x> = 6) พฤติกรรมที่bytesเป็นเพียงstr:

>>> bytes is str
True

PEP 3112 :

2.6 str แตกต่างจากประเภทไบต์ 3.0 ในรูปแบบต่างๆ; สะดุดตาที่สุดคอนสตรัคที่แตกต่างอย่างสิ้นเชิง


0

คำตอบบางข้อใช้ไม่ได้กับจำนวนมาก

แปลงจำนวนเต็มเป็นตัวแทนฐานสิบหกจากนั้นแปลงเป็นไบต์:

def int_to_bytes(number):
    hrepr = hex(number).replace('0x', '')
    if len(hrepr) % 2 == 1:
        hrepr = '0' + hrepr
    return bytes.fromhex(hrepr)

ผลลัพธ์:

>>> int_to_bytes(2**256 - 1)
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'

1
"วิธีการอื่น ๆ ทั้งหมดไม่สามารถใช้ได้กับคนจำนวนมาก" ไม่เป็นความจริงเลยใช้int.to_bytesงานได้กับจำนวนเต็มใด ๆ
juanpa.arrivillaga

@ juanpa.arrivillaga ใช่ฉันไม่ดี ฉันแก้ไขคำตอบของฉันแล้ว
Max Malysh

-1

หากคำถามคือวิธีการแปลงจำนวนเต็มเอง (ไม่ใช่สตริงเทียบเท่า) เป็นไบต์ฉันคิดว่าคำตอบที่มีประสิทธิภาพคือ:

>>> i = 5
>>> i.to_bytes(2, 'big')
b'\x00\x05'
>>> int.from_bytes(i.to_bytes(2, 'big'), byteorder='big')
5

ข้อมูลเพิ่มเติมเกี่ยวกับวิธีการเหล่านี้ที่นี่:

  1. https://docs.python.org/3.8/library/stdtypes.html#int.to_bytes
  2. https://docs.python.org/3.8/library/stdtypes.html#int.from_bytes

1
สิ่งนี้แตกต่างจากคำตอบของ brunsgaard โพสต์เมื่อ 5 ปีที่แล้วและปัจจุบันเป็นคำตอบที่ได้รับการโหวตสูงสุดหรือไม่
Arthur Tacca
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.