0

I'm making an encryption tool in Python, and part of the process is to reassign each position with the encrypted position. The code is meant to run encryption on each character this is achieved through this code:

for pos in range (0, len(plaintext)-1):
    print("pos is %s" % (pos))

The following code should be irrelevant but can be supplied. When this code is run, the output is,

pos is 0
pos is 1

The loop never reaches 2, despite the length of 'plaintext' being 3. If I were to remove the '-1' part (included as length and final position are not the same), I am met with an error, that the following code is out of range (the following code in my eyes doesn't need to be changed, however can be supplied).

Can anyone understand or explain my issue?

Python 2.7.11 Windows 8.1

EDIT:

The code following the loop start is as follows,

for pos in range (0, len(plaintext)-1):
    print("pos is %s" % (pos))
    for k in range (0, key):
        if(plaintext[pos] == "z"):
            crypt = "%s%s%s" % (crypt[:pos], "a", crypt[pos+1:])
        else:
            crypt = "%s%s%s" % (crypt[:pos], abc[abc.index(plaintext[pos])+1], crypt[pos+1:])

The error was raised due to the slicing being crypt[pos+1:]

because you cannot assign to a position in 2.7.11, so my workaround was to concatenate either 'side' of my position, around the newly encrypted key.

teknoboy
  • 175
  • 1
  • 2
  • 12
oisinvg
  • 592
  • 2
  • 8
  • 21
  • 1
    show us `plaintext` if you want help with this – jacoblaw Jul 19 '17 at 20:40
  • 7
    `range()` does not include the `stop` value, so `range(len(plaintext))` is sufficient. Note: `for pos, char in enumerate(plaintext):` is probably better. – AChampion Jul 19 '17 at 20:40
  • `range(len(plaintext))` – cs95 Jul 19 '17 at 20:40
  • The end of the range is exclusive: the last index you specify is the first index *not* included in the range. So you should not subtract 1 from the length of the string. As for the error you get, it's not caused by the code you posted so I won't guess at the cause. – kindall Jul 19 '17 at 20:41
  • https://stackoverflow.com/questions/538346/iterating-over-a-string#answer-4547728 There are other answers in this particular post but this one is probably best. – scrappedcola Jul 19 '17 at 20:42
  • The code you submitted seems to have no problems other than the -1; I think you do need to supply the relevant parts of the rest of the code. – jmcampbell Jul 19 '17 at 20:44
  • 1
    I would imagine `abc[abc.index(c)+1]` is throwing the `IndexError`, please include the error. You probably need a modulus operator somewhere. – AChampion Jul 19 '17 at 20:53
  • In addition to the traceback, could you also show the code where you define `abc`, `plaintext`, `crypt`, and `key`? – jmcampbell Jul 19 '17 at 21:00
  • Not sure why the slice would create an "out of range" error: `'abc'[100:] == ''` – AChampion Jul 19 '17 at 21:02

4 Answers4

0

range(lower, upper) returns a list that contains values starting at lower and ending at upper-1. If len(plaintext) == 3, then your list correctly contains the elements 0 and 1.

As for your error, you haven't included the relevant code.

rlbond
  • 65,341
  • 56
  • 178
  • 228
0

range(3) gives you the sequence [0, 1, 2], so in your example you would want to have range(len(plaintext)) (the lower bound defaults to zero anyway).

However, the pythonic way to go over every char in a string (assuming that's your actual goal) would be for c in plaintext. To also have an enumerator (get both the character and the index) use:

for i, c in enumerate(plaintext):
    print('pos is %s" % (i))
alxwrd
  • 2,320
  • 16
  • 28
teknoboy
  • 175
  • 1
  • 2
  • 12
0

If the length of plaintext is 3, then when you do len(plaintext)-1 that equals to 2. Setting a range as range(0,2) will always result in 0 and 1 only. You can test that using this line:

print(list(range(0, len(plaintext)-1)))

Which will output only [0,1]

In other words, the second argument to range is the top limit but is not itself included

Now, for your 2nd part - it would seem that when you do reach 2, somewhere in the rest of your code there's something being accessed that has only 2 items in it, so giving it an index of 2 is searching for the 3rd item (because we start indexing at 0) and then you get the error you describe (index out of range). I can't know exactly why that is without seeing your code.

Ofer Sadan
  • 11,391
  • 5
  • 38
  • 62
0

As others have said, the end of the range is exclusive, meaning that the correct for loop is:

for pos in range(len(plaintext)):

In the second part of your code, assuming that plaintext and crypt are the same length, crypt[pos+1:] returns an error on the last loop. For example, if the length of plaintext is 3, pos is 2 on the last loop, and crypt[3] indexes past the end of the string. To solve this, you could use a ternary operator:

end = crypt[pos+1:] if pos < len(crypt) else ""
crypt = "%s%s%s" % (crypt[:pos], abc[abc.index(plaintext[pos])+1], end)

This makes it so that crypt[pos+1:] is only executed if pos < len(crypt).

jmcampbell
  • 378
  • 4
  • 17