How to safely truncate a quoted string?

Viewed 168

I have the following string:

Customer sale 88% in urm 50

Quoted with urllib.parse.quote, it becomes:

Customer%20sale%2088%25%20in%20urm%2050%27

Then I need to limit its length to a maximum of 30 characters and I use value[:30].

The problem is that it becomes "Customer%20sale%2088%25%20in%" which is not valid:
The last % is part of %20 from quoted string and makes it an invalid quoted string.

I don't have control over the original string, and the final result needs to have a maximum 30 length, so I can't truncate it beforehand.

What approach would be feasible?

4 Answers

urllib.quote uses percent-encoding as defined in RFC 3986. This means that encoded character will always be of the form "%" HEXDIG HEXDIG.

So you simply can delete any trailing rest of the encoding by looking for a % sign in the last two characters.

For example:

>>> s=quote("Customer sale 88% in urm 50")[:30]
>>> n=s.find('%', -2)
>>> s if n < 0 else s[:n]
'Customer%20sale%2088%25%20in'

What about looking for dangling percentage marks?

value = value[:30]
if value[-1] == "%":
    value = value[:-1]
elif value[-2] == "%":
    value = value[:-2]
print(value)

The encoded string will be always in the format of %HH. You want the string length to be maximum of 30characters with a valid encoding. So, probably the best solution I can think of:

from urllib.parse import quote
string= "Customer sale 88% in urm 50"
string=quote(string)
string=string[:string[:30].rfind("%")]
print(string)

Output:

string=string[:string[:30].rfind("%")]

Solution:

After the encoding, you may get a string of any length, the following one line of code will be enough to achieve your requirement in a very optimized way.

 string=string[:string[:30].rfind("%")]

Explanation:

It first extracts 30 characters from the quoted string then searches for % from the right end. The position of % from the right end will be used to extract the string. Voilaa!! You got your result.

Alternate approach:

Instead of string=string[:string[:30].rfind("%")] you can do like this too string=string[:string.rfind("%",0,30)]

Note: I extracted the string and stored it back to showcase how it works, if you do not want to store then you can simply use like print(string[:string[:30].rfind("%")]) to display the results

enter image description here

Hope it helps...

How about putting the individual characters in a list and then count and strip? Rough example:

from urllib import quote

s = 'Customer sale 88% in urm 50'

res = []
for c in s:
    res.append(quote(c))

print res # ['C', 'u', 's', 't', 'o', 'm', 'e', 'r', '%20', 's', 'a', 'l', 'e', '%20', '8', '8', '%25', '%20', 'i', 'n', '%20', 'u', 'r', 'm', '%20', '5', '0']
print len(res)

current_length = 0
for item in res:
    current_length += len(item)

print current_length # 39

while current_length > 30:
    res = res[:-1]
    current_length = 0
    for item in res:
        current_length += len(item)

print "".join(res) # Customer%20sale%2088%25%20in

That way you will not end up cutting in the middle of a quoting character. And in case you need a different length in the future, you just need to modify the while-loop. Well, code can be made more clean as well ;)

Related