-3

I'm trying to print the usernames that contain only letters, numbers, "-" and "_" and are between 3 and 16 characters long.

usernames = input().split(', ')

for word in usernames:
    if 3 <= len(word) <= 16 and (c for c in word if (c.isalnum() or c == '_' or c == '-')) and ' ' not in word:
        print(word)

Input:

Jeff, john45, ab, cd, peter-ivanov, @smith

Output must be:

Jeff
John45
peter-ivanov

But instead is:

Jeff
john45
peter-ivanov
@smith

Why is that so?

prsnr
  • 83
  • 7
  • 5
    You're using a generator `(c for c in word if (c.isalnum() or c == '_' or c == '-'))` as a condition. It's not checking each item in the generator. The generator object is truthy, so your condition is true. – khelwood Nov 12 '21 at 22:52
  • 1
    That expression generates a list containing all characters that match those criteria. It's Boolean value will be true if even one letter meets your criteria. – Chris Nov 12 '21 at 22:53
  • 1
    `all(True if (c.isalnum() or c == '_' or c == '-') else False for c in word )` Seeing as your current conditions have no way to return false for when invalid characters are in the word – fam-woodpecker Nov 12 '21 at 22:57

1 Answers1

2

(c for c in word if (c.isalnum() or c == '_' or c == '-')) is a generator containing all those characters. All generators are truthy, so this is not actually checking anything.

Use the all() function to test if all the characters fit that criteria. And then there's no need to check ' ' not in word, since that doesn't meet this criteria.

    if 3 <= len(word) <= 16 and all(c.isalnum() or c == '_' or c == '-' for c in word):

You could also also use a regular expression:

import re

for word in usernames:
    if re.match(r'[\w-]{3,}$', word):
        print(word)
Barmar
  • 741,623
  • 53
  • 500
  • 612