tags:

views:

212

answers:

7

Does anybody have a quickie for converting an unsafe string to an int?

The string typically comes back as: '234\r\n' or something like that.

In this case I want 234. If '-1\r\n', I want -1. I never want the method to fail but I don't want to go so far as try, except, pass just to hide errors either (in case something extreme happens).

+7  A: 

int('243\r\n'.strip())

But that won't help you, if something else than a number is passed. Always put try, except and catch ValueError.

Anyway, this also works: int('243\n\r '), so maybe you don't even need strip.

EDIT:

Why don't you just write your own function, that will catch the exception and return a sane default and put into some utils module?

gruszczy
+1  A: 
try:
    x=int("234\r\n".strip())
except ValueError:
    x=0
Xavier Combelle
In this situation, I might as well just do:try: x=int("234\r\n")except ValueError: x=0
Adam Nelson
Yep; you might as well, unless you know there are other kinds of strings you need to accept.
Ian Clelland
A: 

You could just:

def to_int(unsafe_string):
    return int(unsafe_string.strip())

But it will throw a ValueError if the stripped unsafe_string is not a valid number. You can of course catch the ValueError and do what you like with it.

mojbro
I guess I was hoping that something like this was available natively. Maybe in Django?
Adam Nelson
As gruszczy mentioned, it seems like `int('234\r\n')` works without strip, so my function here is completely redundant.
mojbro
There is nothing existing natively because Explicit is better than implicit. (from Zen of python)
Xavier Combelle
+1  A: 
import re
int(re.sub(r'[^\d-]+', '', your_string))

This will strip everything except for numbers and the "-" sign. If you can be sure that there won't be ever any excess characters except for whitespace, use gruszczy's method instead.

Ludwik Trammer
This actually turned out to be best. I was getting bitten by things like '1234\r\nm' which killed int()
Adam Nelson
+6  A: 

In this case you do have a way to avoid try/except, although I wouldn't recommend it (assuming your input string is named s, and you're in a function that must return something):

xs = s.strip()
if xs[0:1] in '+-': xs = xs[1:]
if xs.isdigit(): return int(s)
else: ...

the ... part in the else is where you return whatever it is you want if, say, s was 'iamnotanumber', '23skidoo', empty, all-spaces, or the like.

Unless a lot of your input strings are non-numbers, try/except is better:

try: return int(s)
except ValueError: ...

you see the gain in conciseness, and in avoiding the fiddly string manipulation and test!-)

I see many answers do int(s.strip()), but that's supererogatory: the stripping's not needed!

>>> int('  23  ')
23

int knows enough to ignore leading and trailing whitespace all by itself!-)

Alex Martelli
I have to give this answer status simply for the use of the word 'supererogatory'
Adam Nelson
Rather than being supererogatory, perhaps the strip() is a case of "explicit is better than implicit".
dangph
@dangph, _redundant_ operations are best not done at all;-).
Alex Martelli
@Alex, it's not redundant because it communicates the intent of the programmer. It serves a purpose. It says that the programmer has considered that particular case. If it were left implicit, then the reader of the code would not know that.
dangph
Actually, if reader knows how int behaves (now we all know ;-)), then he knows the intent of a programmer. Doing certain code twice is truly redundant.
gruszczy
@gruszczy, the reader doesn't in general know that int() behaves that way. The reader also doesn't know whether the programmer knows or does not know. There is a great deal of uncertainty.
dangph
An in the face of ambiguity refuse the temptation to guess and read the docs ;-)
gruszczy
@gruszczy, I use 3 or 4 different languages at my job. I don't want to have to learn highly contingent facts about string-to-int functions (whether they do an implicit strip or not) if I don't have to. There's no reason to expect that they should or should not do an implicit strip. Worse, I don't know if you have read the docs. That means if I read your code, I have to check two cases instead of one. That slows me down. That means the code is less good than it could be. That's why they say "explicit is better than implicit".
dangph
@dangph, I know Tim Peters (the author of the Zen of Python) quite well, and I can assure you that's *definitely* **not** what he meant by "explicit is better than implicit". Code that uses a language well (in particular by not doing redundant operations, which clutter code, reducing its readability, and damage performance) is _better_ than code which uses that language badly (e.g. through redundancy). If you don't know well the programming languages you're using, learn them -- there's nothing "contingent" about the behavior of `int`, which hasn't changed in 10 years or more!
Alex Martelli
@dangph: It took me a single command in `ipython` to check, that `int` works the way it does. Look on the other side - what would a seasoned python developer think, when he saw, that you redundantly strip a string, before coercing it to int? I guess he'd be a little startled and would go to the author with a WTF question. Either way you will make it less readable for someone, so it's better to have it done at least right.
gruszczy
@Alex Martelli, the behavior of `int` was contingent upon the whim of the person who designed it. Mathematically speaking, if an argument to function isn't in the domain of that function, then the result is undefined. Can one say that " 123 " is intrinsically in the domain of all string-to-int functions? I don't believe one can say that. The designer could have thrown a bad-argument exception in that case. Instead they chose to broaden the domain to include such arguments. That's what I mean by 'contingent'. My argument is that because of that contingency, an explicit `strip` is not redundant.
dangph
@dangph, "practicality beats purity" is at the heart of the Zen of Python: pedantically throwing an exception (`ValueError` of course;-) for such innocuous, frequently occurring surrounding whitespace would have been impractical, thus unpythonic, forcing exactly the `strip` call you so adore -- thus showing that said call goes against the spirit and the practice of Python, and is a really bad idea. Like, say, `'ba%sju%s' % (str(x), str(y))`, for example, is horrible practice: "mathematically speaking", no reason `%s` should `str` its arg, but it would be horribly unPythonic if it didn't!-)
Alex Martelli
@Alex Martelli, I will have to disagree with you that implicit-strip is a case of "practicality beats purity" and that it doesn't violate "special cases aren't special enough to break the rules", but in any case I think that is beside the point. I'm not talking about how `int` should have been designed. It is what it is. I'm talking about the problem of the reader of the code having to carry around a little table in their head about which languages have implicit-strip: Python: yes, C#: yes, Java: no, Delphi: no, etc.
dangph
It don't think the addition of `strip()` impairs the readability of the code, and in fact it tells the reader that whoever wrote the code _expected_ there to be whitespace around the integer. The redundancy doesn't add any significant amount of overhead (check it out with `timeit`) so one cannot use that as a basis for their argument unless they are processing billions of integers. In my mind both ways of writing it are "correct".
tgray
`timeit` tells me the overhead of `int(x.strip())` vs `int(x)` lies beteween 63% and 84% -- way plenty as a "basis for argument": the Pythonic uses of the language, i.e. the good and frequent ones that the language's developers want to encourage and support (rather than discourage!), are obviously the one that get the most optimization attention, so speed results often tell you a lot about what Guido, I, and dozens of other Python core contributors, approve of, support, and actively encourage. But I'm sure you know better than the language's core contributors, right?-)
Alex Martelli
@Alex Martelli, that's an astonishingly poor argument. I don't know what to say. And invoking yourself in an Argument from Authority? Really?
dangph
@Alex, I didn't mean to imply that I know more about python than you. Am I using `timeit` wrong? `1 - (timeit("int(' 1234 \\r\\n')") / timeit("int(' 1234 \\r\\n'.strip())"))` gives me `13%` on my machine and a difference of around 0.35 seconds for 1 million cycles. Maybe the application defines whether or not this is significant enough to refactor code that uses `strip()` redundantly.
tgray
@tgray, w/`python -mtimeit -s'x=" 23 "' 'int(x)'` I measure a range of 0.297 to 0.329 usec, w/`python -mtimeit -s'x=" 23 "' 'int(x.strip())'`, 0.535 to 0.546 -- `timeit` is best used at a shell prompt w/`python -mtimeit -s'<setup>' 'code'`, btw. These numbers indicate 63% to 84% overhead for the redundant `.strip` call (call it 68% +- 5%, roughly).
Alex Martelli
@tgray, absolutely the application should define whether to use some particular micro-optimization or not. Personally if I needed to do that in this case, I would put a comment on it: `# int() does strip`. But if I didn't have to, I would prefer the explicit `strip` because code is better than comments. Comments can lie but code cannot.
dangph
A: 

Without knowing what kinds of inputs you are expecting, it's hard to say what is 'enough' to solve this. If you are just worried about trailing newlines, then Xavier Combelle's or gruszczy's answer is enough:

try:
    x = int(value)
except ValueError:
    x = 0

If you have to find any integer, anywhere in a string, then you might need something like this:

import re
try:
    x = int(re.search(r'(0|(-?[1-9][0-9]*))', searchstring).group(0))
except TypeError: # Catch exception if re.search returns None
    x = 0
Ian Clelland
A: 

Under Python 3.0 (IDLE) this worked nicely

strObj = "234\r\n"
try:
    #a=int(strObj)
except ValueError:
    #a=-1

print(a) >>234


If you're dealing with input you cannot trust, you never should anyway, then it's a good idea to use try, except, pass blocks to control exceptions when users provide incompatible data. It might server you better to think about try, except, pass structures as defensive measures for protecting data compatibility rather than simply hidding errors.

try:
    a=int(strObj) #user sets strObj="abc"
except ValueError:
    a=-1

if a != -1:
    #user submitted compatible data
else:
    #inform user of their error

I hope this helps.

codezealot