Python script to convert octal to string eg octal(755) to (rwxr-xr-x). Stuck at adding the dash seperator

Karimi254 :

Below is the script printing the converted octal to string. I would appreciate suggestion on how to add the - separator to the string on each permission (r/w/x)

def octal_to_string(octal):
    result = ""
    value_letters = [(4,"r"),(2,"w"),(1,"x")]
    #Iterating over each digit in octal
    for digit in [int(n) for n in str(octal)]:

        #Checking for each of permission values
        for value, letter in value_letters:
            if digit >= value:
                result += letter
                digit -= value
            else:
                pass
    return result

I currently get:

In [7]: octal_to_string(755)
Out[7]: 'rwxrxrx'

In [8]: octal_to_string(644)
Out[8]: 'rwrr'
Martijn Pieters :

For a quick fix, just replace the pass with result += '-'; for every permission test that is False you can simply insert a dash:

for value, letter in value_letters:
    if digit >= value:
        result += letter
        digit -= value
    else:
        result += '-'

I'd just use bit masking however, with the & bitwise and operator:

for value, letter in value_letters:
    result += letter if digit & value else '-'

This works because 4, 2 and 1 are the decimal (and octal) values for numbers with each one of the 3 bits set to 1, the others to 0. digit & 4 is only going to produce 4 or 0, depending on the value of the 3rd bit from the right being set in digit or not. Since 0 is false, and 4 is a true value, this lets you test if the right bit is set and so decide between the permission letter and -. I used a conditional expression to return either in a single line.

Next, I'd not use a list comprehension to convert the input value into octal digits; just use map() here to lazily convert without creating an additional list object:

for digit in map(int, str(octal)):

or you could divide the input value by ten and take the modulus of 10 to get the digit; you can do so in one step with the divmod() function:

for _ in range(3):
    octal, digit = divmod(octal, 10)

I used a for loop that iterates 3 times here because the octal value for permissions should always consist of 3 octal digits (9 bits). This lets you accept longer values gracefully, just ignoring any digits beyond the right-most 3.

Next, try to avoid using string concatenation in a loop; although Python has some optimisations in place to avoid the worst case performance in simple situations, you can easily miss out on that optimisation. Best to append characters to a list then use str.join() to create the final string in one step.

Put together into a complete function:

def octal_to_string(octal):
    result = []
    for _ in range(3):
        octal, digit = divmod(octal, 10)
        for value, letter in ((4, "r"), (2, "w"), (1, "x")):
            result.append(letter if digit & value else "-")
    return "".join(result)

Demo:

>>> octal_to_string(755)
'r-xr-xrwx'
>>> octal_to_string(644)
'r--r--rw-'

Now, what the function accepts are decimal numbers, not octal numbers. From an API design you'd really want to either accept strings only (containing digits representing an octal value), or if you do accept an integer, then you should treat that value as decimal and use 0o octal notation:

>>> 0o755  # actual octal number
493
>>> format(0o755, 'o')  # string representing value in octal notation
'755'

If you do so, just change divmod(octal, 10) into divmod(octal, 8) to get the octal numbers:

>>> octal = 0o755
>>> for _ in range(3):
...     octal, digit = divmod(octal, 8)
...     print(digit)
...
...
5
5
7

The advantage is that you can then use other notations to specify the permission value (in hex it'd be 0x1ed) or pass in the st_mode attribute of a os.stat() call directly to the function.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=3880&siteId=1