Changing parts of a string with '#' - python

Wanted to see if I'm going in the right direction. I have to change everything but the last four characters of a string into #. I've got two ideas so far.
First one:
def maskify(cc):
cc = raw_input("Enter passcode: ")
n = len(cc)
cc.replace(cc[0:n-4], #) # this one gives me the unexpected EOF while parsing error
Second one (I think this one's closer because it supposedly needs an algorithm):
def maskify(cc):
cc = raw_input("Enter passcode: ")
n = len(cc)
for i in range (0, n-4): # i think a for loop would be good but i don't know how i'm going to use it yet
cc.replace( #not entirely sure what to put here
pass

cc = raw_input("Enter passcode: ")
cc = ''.join(('#' * (len(cc) - 4), cc[-4:]))

The problem in the first example is that the # is unquoted. You need to change it to '#' otherwise it is parsed as the start of a comment and the enclosing parenthesis is a part of that comment. Although, this will only fix the parsing error.
The problem with strings is that you can't change characters inside of them (they are immutable). A common way to get around this is to create an array of the string, change the characters you want to change and then convert the array back to a string (often using ''.join(character_array)). Try that!

How about the following?
def maskify() :
cc = input("Enter passcode: ")
mask = '#'*(len(cc)-4)
return mask + cc[-4:]
I'm not sure how the flow of the rest of your program works, but I doubt whether you should be prompting for raw_input inside of this function. You can decide that depending on your needs. The alternative would look something like this:
def maskify(cc) :
return '#'*(len(cc)-4) + cc[-4:]
myInput = input("Enter passcode: ")
maskedInput = maskify( myInput )
NB: python2 uses raw_input instead of input

Just a little change to your own code:
cc = raw_input("Enter passcode: ")
n = len(cc)
c=""
for i in range(0, n-4): # i think a for loop would be good but i don't know how i'm going to use it yet
c+="#" #not entirely sure what to put here
cc= c+cc [-4:]
print cc
output:
Enter passcode: kased
#ased

The following solution makes the assumption that this would have a security type use, as such passcodes of 4 or fewer characters should just be hashed, otherwise someone would know the whole passcode.
def maskify(cc):
if len(cc) < 9:
split = [0,1,2,3,4,4,4,4,4][len(cc)]
else:
split = len(cc) - 4
return "#" * split + cc[split:]
for length in range(1,12):
test = string.lowercase[:length]
print "%s > %s" % (test, maskify(test))
Giving the following results:
a > #
ab > ##
abc > ###
abcd > ####
abcde > ####e
abcdef > ####ef
abcdefg > ####efg
abcdefgh > ####efgh
abcdefghi > #####fghi
abcdefghij > ######ghij
abcdefghijk > #######hijk
If the short hash is not required, then simply change the array as follows to get the other results:
def maskify(cc):
if len(cc) < 9:
split = [0,0,0,0,0,1,2,3,4][len(cc)]
else:
split = len(cc) - 4
return "#" * split + cc[split:]
Giving:
a > a
ab > ab
abc > abc
abcd > abcd
abcde > #bcde
abcdef > ##cdef
abcdefg > ###defg
abcdefgh > ####efgh
abcdefghi > #####fghi
abcdefghij > ######ghij
abcdefghijk > #######hijk

The string literal '#' is not the same as the character # which starts an inline comment.
def maskify(cc):
cc = raw_input("Enter passcode: ")
mask = '#'*(len(cc)-4)
return mask + cc[-4:]

As others have mentioned # starts a comment. If you want a string containing a hash you need to do '#'.
As André Laszlo mentioned, Python strings are immutable, so it's impossible for a string operation to change a string's content. Thus the str.replace() method can't change the original string: it needs to create a new string which is a modified version of the original string.
So if you do
cc = 'cat'
cc.replace('c', 'b')
then Python would create a new string containing 'bat' which would get thrown away because you're not saving it anywhere.
Instead, you need to do something like
cc = 'cat'
cc = cc.replace('c', 'b')
This discards the original string object 'cat' that was bound to the name cc and binds the new string 'bat' to it.
The best approach (AFAIK) to solving your problem is given in bebop's answer. Here's a slightly modified version of bebop's code, showing that it handles short strings (including the empty string) correctly.
def maskify(s) :
return '#' * (len(s) - 4) + s[-4:]
alpha = 'abcdefghij'
data = [alpha[:i] for i in range(len(alpha)+1)]
for s in data:
print((s, maskify(s)))
output
('', '')
('a', 'a')
('ab', 'ab')
('abc', 'abc')
('abcd', 'abcd')
('abcde', '#bcde')
('abcdef', '##cdef')
('abcdefg', '###defg')
('abcdefgh', '####efgh')
('abcdefghi', '#####fghi')
('abcdefghij', '######ghij')

First of all strings are immutable(they can't be changed) once created--so you can't change the value of cc with replace(). To change all parts of the string except the last four, do this:
def doso(text):
assert len(str(text))>4, 'Length of text should be >4'
ctext=str(text).replace(str(text)[:(len(str(text))-4)],'#'*(len(str(text))-4))
print(ctext)
>>>doso(123) prints 'Length of text should be >4' because len(text)== 3 which is what we expect
Hovever,
>>>doso(12345678) prints #####6789 which is exactly what we expect
Note: Doing '#' * (len(str(text))-4)) accounts for the number of characters we want to replace

# return masked string
def maskify(cc):
maskify = cc
maskifyL= len(maskify) - 4
char = ""
a=0
if maskifyL <= 2:
c2 = cc
else:
for a in range(maskifyL):
char += "#"
a += 1
c2 = maskify.replace(maskify[:maskifyL], char, maskifyL)
return c2

Related

String incrementation

I've just started to learn Python and I'm doing some exercises in codewars. The instructions are simple: If the string already ends with a number, the number should be incremented by 1.
If the string does not end with a number. the number 1 should be appended to the new string.
I wrote this:
if strng[-1].isdigit():
return strng.replace(strng[-1],str(int(strng[-1])+1))
else:
return strng + "1"
return(strng)
It works sometimes (for example 'foobar001 - foobar002', 'foobar' - 'foobar1'). But in other cases it adds 1 to each number at the end (for example 'foobar11' - 'foobar22'), I would like to achieve a code where the effect is to add only +1 to the ending number, for example when 'foobar99' then 'foobar100', so the number has to be considered as a whole. I would be grateful for advices for beginner :)!
First, you have to make some assumptions
Assuming that the numerical values are always at the end of string and the first character from the right that is not numeric would mark the end of the non-number string, i.e.
>>> input = "foobar123456"
>>> output = 123456 + 1
Second, we need to assume that number exists at the end of the string.
So if we encounter a string without a number, we need to decide if the python code should throw an error and not try to add 1.
>>> input = "foobar"
Or we decide that we automatically generate a 0 digit, which would require us to do something like
input = input if input[-1].isdigit() else input + "0"
Lets assume the latter decision for simplicity of the explanation.
Next we will try to read the numbers from the right until you get to a non-digit
Lets use reversed() to flip the string and then a for-loop to read the characters until we reach a non-number, i.e.
>>> s = "foobar123456"
>>> output = 123456
>>> for character in reversed(s):
... if not character.isdigit():
... break
... else:
... print(character)
...
6
5
4
3
2
1
Now, lets use a list to keep the digits characters
>>> digits_in_reverse = []
>>> for character in reversed(s):
... if not character.isdigit():
... break
... else:
... digits_in_reverse.append(character)
...
>>> digits_in_reverse
['6', '5', '4', '3', '2', '1']
Then we reverse it:
>>> ''.join(reversed(digits_in_reverse))
'123456'
And convert it into an integer:
>>> int(''.join(reversed(digits_in_reverse)))
123456
Now the +1 increment would be easy!
How do we find the string preceding the number?
# The input string.
s = "foobar123456"
s = s if s[-1].isdigit() else s + "0"
# Keep a list of the digits in reverse.
digits_in_reverse = []
# Iterate through each character from the right.
for character in reversed(s):
# If we meet a character that is not a digit, stop.
if not character.isdigit():
break
# Otherwise, keep collecting the digits.
else:
digits_in_reverse.append(character)
# Reverse, the reversed digits, then convert it into an integer.
number_str = "".join(reversed(digits_in_reverse))
number = int(number_str)
print(number)
# end of string preceeding number.
end = s.rindex(number_str)
print(s[:end])
# Increment +1
print(s[:end] + str(number + 1))
[output]:
123456
foobar
foobar123457
Bonus: Can you do it with a one-liner?
Not exactly one line, but close:
import itertools
s = "foobar123456"
s = s if s[-1].isdigit() else s + "0"
number_str = "".join(itertools.takewhile(lambda ch: ch.isdigit(), reversed(s)))[::-1]
end = s.rindex(number_str)
print(s[:end] + str(int(number_str) + 1))
Bonus: But how about regex?
Yeah, with regex it's pretty magical, you would still make the same assumption as how we started, and to make your regex as simple as possible you have to add another assumption that the alphabetic characters preceding the number can only be made up of a-z or A-Z.
Then you can do this:
import re
s = "foobar123456"
s = s if s[-1].isdigit() else s + "0"
alpha, numeric = re.match("([a-zA-z]+)(\d.+)", s).groups()
print(alpha + str(int(numeric) + 1))
But you have to understand the regex which might be a steep learning, see https://regex101.com/r/9iiaCW/1
One simple solution would be:
Have two empty variables head (=non-numeric prefix) and tail (numeric suffix). Iterate the string normally, from left to right. If the current character is a digit, add it to tail. Otherwise, join head and tail, add the current char to head and empty tail. Once complete, increment tail and return head + tail:
def foo(s):
head = tail = ''
for char in s:
if char.isdigit():
tail += char
else:
head += tail + char
tail = ''
tail = int(tail or '0')
return head + str(tail + 1)
Leading zeroes (x001 -> x002), if needed, left as an exercise ;)
In your string, you need to check if it is alpha numeric or not. if it is alpha numeric, then you need to check the last character, whether it is digit or not.
now if above condition satisfy then you need to get the index of first digit in the string which make a integer number in last of string.
once you got the index then, seperate the character and numeric part.
once done, convert numerical string part to interger and add 1. after this join both character and numeric part. that is your answer.
# your code goes here
string = 'randomstring2345'
index = len(string) - 1
if string.isalnum() and string[-1].isdigit():
while True:
if string[index].isdigit():
index-=1
else:
index+=1
break
if index<0:
break
char_part = string[:index]
int_part = string[index:]
integer = 0
if int_part:
integer = int(''.join(int_part))
modified_int = integer + 1
new_string = ''.join([char_part, str(modified_int)])
print(new_string)
output
randomstring2346
Regex can be a useful tool in python~ Here I make two groups, the first (.*?) is as few of anything as possible, while the second (\d*$) is as many digits at the end of the string as possible. For more in depth explanation see regexr.
import re
def increment(s):
word, digits = re.match('(.*?)(\d*$)', s).groups()
digits = str(int(digits) + 1).zfill(len(digits)) if digits else '1'
return word + digits
print(increment('foobar001'))
print(increment('foobar009'))
print(increment('foobar19'))
print(increment('foobar20'))
print(increment('foobar99'))
print(increment('foobar'))
print(increment('1a2c1'))
print(increment(''))
print(increment('01'))
Output:
foobar002
foobar010
foobar20
foobar21
foobar100
foobar1
1a2c2
1
02
Source
def solve(data):
result = None
if len(data) == 0 or not data[-1].isdigit():
result = data + str(1) #appending 1
else:
lin = 0
for index, ch in enumerate(data[::-1]):
if ch.isdigit():
lin = len(data) - index -1
else:
break
result = data[0 : lin] + str(int(data[lin:]) + 1) # incrementing result
return result
pass
print(solve("Hey123"))
print(solve("aaabbbzzz"))
output :
Hey124
aaabbbzzz1

Hacker rank string separated challenge

I'm trying to solve a hacker rank challenge:
Given a string, s , of length n that is indexed from 0 to n-1 , print its even-indexed and odd-indexed characters as 2 space-separated strings. on a single line (see the Sample below for more detail)
link: https://www.hackerrank.com/challenges/30-review-loop/problem
Error:
for example:
The input "adbecf" should output "abc def"
When I run python Visualizer my code seem to have the correct output.. but on hacker rank it's saying I have the wrong answer. Does anyone know what might be wrong with my code.
This is the code I tried -
class OddEven:
def __init__(self, input_statement):
self.user_input = input_statement
def user_list(self):
main_list = list(user_input)
even = []
odd = []
space = [" "]
for i in range(len(main_list)):
if (i%2) == 0:
even.append(main_list[i])
else:
odd.append(main_list[i])
full_string = even + space + odd
return(full_string)
def listToString(self):
my_string = self.user_list()
return(''.join(my_string))
if __name__ == "__main__":
user_input = str(input ())
p = OddEven(user_input)
print(p.listToString())
First of all, input is always string, you don't need to convert it here.
user_input = str(input())
Each line is provided to you as separate input. Number of strings equal to num in the first line. In this case 2, so...
count = input()
for s in range(int(count)):
...
user_input variable inside user_list function should be accessed as self.user_input, it's a property of an object, which you pass to function as self.
Also you can iterate over list directly.
Here:
full_string = even + space + odd
you're trying to concatenate list, which is not a good idea, you'll still get a list.
You can join list with separating them with some string using join string method.
' '.join(list1, list2, ..., listN)
It's better do define odd and even as empty strings.
And then join them the using concatenation (+).
Here:
if (i%2) == 0
you don't have to compare with 0. Python will evaluate what's to the right from condition as True or False. So:
if i % 2:
...
There is simpler solution:
def divide(self):
odd = even = ''
for i, c in enumerate(self.user_input):
if i % 2:
odd += c
else:
even += c
return even + ' ' + odd
Here is the simple code for this problem:)
T=int(input())
for i in range(0,T):
S=input()
print(S[0::2],S[1::2])

What's the role of string = "" in a program Python

i know the title may not be the best, as i'm not exactly how to explain my problem in short words. However i recently was looking at some codes online and i didn't get the reason why some code was used i tried looking on the internet but as i dont know what that part of the code is called ive no idea what to search up so you guys are my last hope.
In this function
def NumIntoChar(LineLis):
for n in LineLis:
string = "" # Here is what im not sure. why is this used here ?
for i in range(n):
string += '-'
print(string)
Im unsure why string = "" is used between the 2 for looks
another example is:
message = """SAHH""" # Add Code
message = message.upper()
keyShift = 1
encryptedMsg = ""
result = {}
while keyShift <= 26:
encryptedMsg = ""
for character in message:
if character.isalpha() is True:
x = ord(character) - 65
x += keyShift
x = x % 26
encryptedMsg += chr(x + 65)
else:
encryptedMsg += character
result[keyShift] = encryptedMsg
keyShift += 1
for r in result.keys():
print(r,result[r])
Here we see ' encryptedMsg = "" ' being used just like in the previous code.
Just below that line of code, you have this for loop:
for i in range(n):
string += '-'
The x += y operator is syntactic sugar for x = x + y. In order to use this operator, x must have a defined value first.
For the first iteration of the loop, string will essentially be assigned like this:
string = string + '-'
In order to avoid NameError being thrown, string first needs to be declared and assigned some value, which is what string = "" does. The expression in the first iteration of the loop then essentially becomes:
string = '' + '-'
Here you initialize a variable with empty string using var = ''.
It is commonly followed in scenarios where you have to iteratively concatenate content to form a bigger string. Your code starts with initializing the empty string and within the loop, content of the string is concatenated. For example:
my_str = ""
while repeat:
my_str += some_str
# Do some stuff
Other scenario in which you might need it is: when you have to set default value of string as empty, but based on some condition reset the content of string. For example:
my_name = ''
if user.is_logged_in():
my_name = user.name
Also read: Initialize a string variable in Python: “” or None?

Python Case Matching Input and Output

I'm doing the pig latin question that I'm sure everyone here is familiar with it. The only thing I can't seem to get is matching the case of the input and output. For example, when the user enters Latin, my code produces atinLay. I want it to produce Atinlay.
import string
punct = string.punctuation
punct += ' '
vowel = 'aeiouyAEIOUY'
consonant = 'bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ'
final_word = input("Please enter a single word. ")
first_letter = final_word[:1]
index = 0
if any((p in punct) for p in final_word):
print("You did not enter a single word!")
else:
while index < len(final_word) and (not final_word[index] in vowel):
index = index+1
if any((f in vowel) for f in first_letter):
print(final_word + 'yay')
elif index < len(final_word):
print(final_word[index:]+final_word[:index]+'ay')
What you need is str.title(). Once you have done your piglatin conversion, you can use title() built-in function to produce the desired output, like so:
>>> "atinLay".title()
'Atinlay'
To check if a string is lower case, you can use str.islower(). Take a peek at the docs.
simply use the built in string functions.
s = "Hello".lower()
s == "hello"
s = "hello".upper()
s == "HELLO"
s = "elloHay".title()
s == "Ellohay"

Selecting specific int values from list and changing them

I have been playing with Python and came across a task from MIT, which is to create coded message (Julius Cesar code where for example you change ABCD letters in message to CDEF). This is what I came up with:
Phrase = input('Type message to encrypt: ')
shiftValue = int(input('Enter shift value: '))
listPhrase = list(Phrase)
listLenght = len(listPhrase)
ascii = []
for ch in listPhrase:
ascii.append(ord(ch))
print (ascii)
asciiCoded = []
for i in ascii:
asciiCoded.append(i+shiftValue)
print (asciiCoded)
phraseCoded = []
for i in asciiCoded:
phraseCoded.append(chr(i))
print (phraseCoded)
stringCoded = ''.join(phraseCoded)
print (stringCoded)
The code works but I have to implement not shifting the ascii value of spaces and special signs in message.
So my idea is to select values in list in range of range(65,90) and range(97,122) and change them while I do not change any others. But how do I do that?
If you want to use that gigantic code :) to do something as simple as that, then you keep a check like so:
asciiCoded = []
for i in ascii:
if 65 <= i <= 90 or 97 <= i <= 122: # only letters get changed
asciiCoded.append(i+shiftValue)
else:
asciiCoded.append(i)
But you know what, python can do the whole of that in a single line, using list comprehension. Watch this:
Phrase = input('Type message to encrypt: ')
shiftValue = int(input('Enter shift value: '))
# encoding to cypher, in single line
stringCoded = ''.join(chr(ord(c)+shiftValue) if c.isalpha() else c for c in Phrase)
print(stringCoded)
A little explanation: the list comprehension boils down to this for loop, which is easier to comprehend. Caught something? :)
temp_list = []
for c in Phrase:
if c.isalpha():
# shift if the c is alphabet
temp_list.append(chr(ord(c)+shiftValue))
else:
# no shift if c is no alphabet
temp_list.append(c)
# join the list to form a string
stringCoded = ''.join(temp_list)
Much easier it is to use the maketrans method from the string module:
>>import string
>>
>>caesar = string.maketrans('ABCD', 'CDEF')
>>
>>s = 'CAD BA'
>>
>>print s
>>print s.translate(caesar)
CAD BA
ECF DC
EDIT: This was for Python 2.7
With 3.5 just do
caesar = str.maketrans('ABCD', 'CDEF')
And an easy function to return a mapping.
>>> def encrypt(shift):
... alphabet = string.ascii_uppercase
... move = (len(alphabet) + shift) % len(alphabet)
... map_to = alphabet[move:] + alphabet[:move]
... return str.maketrans(alphabet, map_to)
>>> "ABC".translate(encrypt(4))
'EFG'
This function uses modulo addition to construct the encrypted caesar string.
asciiCoded = []
final_ascii = ""
for i in ascii:
final_ascii = i+shiftValue #add shiftValue to ascii value of character
if final_ascii in range(65,91) or final_ascii in range(97,123): #Condition to skip the special characters
asciiCoded.append(final_ascii)
else:
asciiCoded.append(i)
print (asciiCoded)

Categories

Resources