Tour of Go Exercise: rot13Reader

I am trying to follow the exercise Tour of Go rot13Reader :

Here is my solution:

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

func rot13(x byte) byte {
    switch {
    case x >= 65 && x <= 77:
        fallthrough
    case x >= 97 && x <= 109:
        x = x + 13
    case x >= 78 && x <= 90:
        fallthrough
    case x >= 110 && x >= 122:
        x = x - 13
    }
    return x
}

func (r13 *rot13Reader) Read(b []byte) (int, error) {
    n, err := r13.r.Read(b)
    for i := 0; i <= n; i++ {
        b[i] = rot13(b[i])
    }
    return n, err
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

      

Returns You prnpxrq tur poqr!

what only the first word means "Lbh penpxrq gur pbqr!" cracked How can I crack a whole sentence?

+4


source to share


14 replies


EDIT:

Basically your solution is good and works, you just typed 1 character:

case x >= 110 && x >= 122:

      

Change this to:

case x >= 110 && x <= 122:

      


Your entry and exit:

Lbh penpxrq gur pbqr!
You prnpxrq tur poqr!

      

There is a change in every word. The problem is not that only the first word is read and decoded, the problem is in your decoding algorithm.



In ROT13, if the offset is outside the range of letters, you must start at the beginning of the alphabet (or at the end). For example, the offset 'N'

will be 'Z'

+ 1, so it will become the 'A'

first letter. Take a look at this simple character mapping:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm

      

So what you have to do is, after the shift by 13, if the letter goes outside the alphabet, -26

it is by -26

(the number of letters in the English alphabet), which gives the desired effect (so that after the last letter you continue from the first).

Solution example:

func rot13(x byte) byte {
    capital := x >= 'A' && x <= 'Z'
    if !capital && (x < 'a' || x > 'z') {
        return x // Not a letter
    }

    x += 13
    if capital && x > 'Z' || !capital && x > 'z' {
        x -= 26
    }
    return x
}

      

And its output:

You cracked the code!

      

Try this on the Go Playground .

+11


source


My code also works, it's easier!



func (rd *rot13Reader) Read(b []byte) (int, error) {
    m := make(map[byte]byte)
    input := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    output := "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
    for idx := range input {
        m[input[idx]] = output[idx]
    }

    n, err := rd.r.Read(b)
    for i := 0; i < n; i++ {
        if val, ok := m[b[i]]; ok {
          b[i] = val
        }
    }
    return n, err
}

      

+2


source


You can use instead:

func rot13(x byte) byte {
    input := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
    output := []byte("NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm")
    match := bytes.Index(input, []byte{x})
    if match == -1 {
        return x
    }
    return output[match]
}

      

+2


source


The problem is that your function isn't working the way you want it to. To verify this, just try running your function on "Lbh penpxrq Lbh gur pbqr!"

. As you can see, the first word is decoded (as well as the third). So this means that your function doesn't only run on the first word, but actually runs on all words (it just happens that nothing has changed).

func (rot *rot13Reader) Read(p []byte) (n int, err error) {
    n, err = rot.r.Read(p)
    for i := 0; i < len(p); i++ {
        if (p[i] >= 'A' && p[i] < 'N') || (p[i] >='a' && p[i] < 'n') {
            p[i] += 13
        } else if (p[i] > 'M' && p[i] <= 'Z') || (p[i] > 'm' && p[i] <= 'z'){
            p[i] -= 13
        }
    }
    return
}

      

And so . The code is taken from here .

+1


source


I prefer to manipulate straight integers in the rot13 function

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

const a int = int('a')
const z int = int('z')

const A int = int('A')
const Z int = int('Z')

func rot13(b int) int {

    isLower := b >= a && b <= z
    isUpper := b >= A && b <= Z

    if isLower {
        return a + ((b - a + 13) % 26)
    }

    if isUpper {
        return A + ((b - A + 13) % 26)
    }

    return b
}

func (rot *rot13Reader) Read(b []byte) (int, error) {
    n, err := rot.r.Read(b)
    if err == io.EOF {
        return 0, err
    }

    for x := range b {
        b[x] = byte(rot13(int(b[x])))
    }
    return n, err
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

      

+1


source


func rot13(x byte) byte {
    if (x >=  'A' && x <= 'M') || (x >=  'a' && x <= 'm') {
        return x + 13
    } else if (x >=  'N' && x <= 'Z') || (x >=  'n' && x <= 'z') {
        return x - 13
    } else {
        return x
    }
}

      

+1


source


As an answer to a question from @icza, not hardcoding whether a byte is an alphabet or not, create a little function to determine it. This makes the code much cleaner.

func alphabetic(x byte) bool {
    return (x >= 'A' && x <='Z') || (x >= 'a' && x <= 'z')
}

func capital(x byte) bool {
    return x >= 'A' && x <= 'Z'
}

func rot13(x byte) byte {
    // byte isn't a letter
    if !alphabetic(x) {
        return x
    }

    original_is_capital := capital(x)

    // apply rot13  
    x += 13

    // the new letter should loop around
    if !alphabetic(x) || original_is_capital {
        x -= 26
    }

    return x
}

      

0


source


Another way to do it:

func (rr *rot13Reader) Read(b []byte) (int, error) {
    n, err := rr.r.Read(b)
    if err != nil {
        return 0, err
    }
    for i := 0; i < n; i++ {
        if b[i] >= 'A' && b[i] <= 'z' {
            b[i] += 13
            if b[i] > 'z' {
                b[i] -= 26
            }
        }
    }
    return n, nil
}

      

0


source


It's been two years since the question was asked, but I want to share my code which was very simple compared to.

func (rot rot13Reader) Read(p []byte) (n int, err error) {
    n, err = rot.r.Read(p)  // read the string into byte array, n holds the length
    for i := 0; i < n; i++ {
        upper := p[i] >='A'||p[i]<='Z' //new
        lower := p[i]>='a'||p[i]<='z'  //new
        if (upper||lower){ //new
            p[i] += 13   // we are simply decoding here
            if (upper && p[i] > 'Z') || (lower && p[i] > 'z') { //new
                p[i] -= 26 // and semi-circle the table if it goes beyond
            }
        }
    }
    return
}

      

result? You cracked the code.

Of course, it would be better to implement the entire ASCII scale, but it still does most of the work.


EDIT : After a year and a half, editing this post resulted in someone else editing the bug. Editing still keeps the code simple by providing a few additional conditions. Moreover, one can easily turn this code into rot13 encoder. The previous output wasYou-cracked-the-code.

-1


source


func (reader rot13Reader) Read(bytes []byte) (int, error) {
    n, err := reader.r.Read(bytes)
    for i, val := range bytes[:n] {
        if val >= 'a' && val <= 'z' {
            bytes[i] = (val-'a'+13)%26 + 'a'
        } else if val >= 'A' && val <= 'Z' {
            bytes[i] = (val-'A'+13)%26 + 'A'
        }
    }
    return n, err
}

      

-1


source


This is how I solved it:

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

func rot13(r byte) byte {
    sb := rune(r)
    if sb >= 'a' && sb <= 'm' || sb >= 'A' && sb <= 'M' {
        sb = sb + 13
    }else if sb >= 'n' && sb <= 'z' || sb >= 'N' && sb <= 'Z' {
        sb = sb - 13
    }
    return byte(sb)

}

func (r13 *rot13Reader) Read(b []byte) (i int, e error) {
    n, err := r13.r.Read(b)
    for i := 0; i <= n; i++ {
        b[i] = rot13(b[i])
    }
    return n, err
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

      

-1


source


Here are my two cents:

(Note that the ASCII code "Z" is 90 and "h" is 114)

func (r rot13Reader) Read(p []byte) (n int, err error) {
    n, err = r.r.Read(p)
    if err == nil {
        for i := range p {
            if c := p[i] + 13; c > 'z' || (c > 'Z' && c < 'h') {
                p[i] = c - 26
            } else {
                p[i] = c
            }
        }
    }
    return n, err
}

      

-1


source


My attempt:

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    io.Reader
}

func (r rot13Reader) Read(p []byte) (n int, err error) {
    n, _ = r.Reader.Read(p)
    for i := 0; i < n; i++ {
        if p[i] != byte(' ') {
            if p[i] > byte('m') {
                p[i] = p[i] - 13
            } else {
                p[i] = p[i] + 13
            }
        }
    }
    err = io.EOF
    return
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    _, _ = io.Copy(os.Stdout, &r)
}

      

-1


source


My solution was like this:

func rot13(b byte) byte {
    in := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
    out := []byte("NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm")

    for i, v := range in {
        if v == b {
            return out[i]
        }
    }

    return b
}

type rot13Reader struct {
    r io.Reader
}

func (rot *rot13Reader) Read(b []byte) (int, error) {
    n, err := rot.r.Read(b)

    for i := range b {
        b[i] = rot13(b[i])
    }

    return n, err
}

      

-1


source







All Articles