diff --git a/19-strings-runes-bytes/01-bytes-runes-strings-basics/main.go b/19-strings-runes-bytes/01-bytes-runes-strings-basics/main.go new file mode 100644 index 0000000..74a1bcd --- /dev/null +++ b/19-strings-runes-bytes/01-bytes-runes-strings-basics/main.go @@ -0,0 +1,57 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import "fmt" + +func main() { + str := "hey" + bytes := []byte{104, 101, 121} + + // same as: []byte("hey") + fmt.Printf(`"hey" as bytes : %d`+"\n", []byte(str)) + + // same as: string([]byte{104, 101, 121}) + fmt.Printf("bytes as string : %q\n", string(bytes)) + + // runes are unicode codepoints (numbers) + fmt.Println() + fmt.Printf("%c : %[1]d\n", 'h') + fmt.Printf("%c : %[1]d\n", 'e') + fmt.Printf("%c : %[1]d\n", 'y') + + // a rune literal is typeless + // you can put it in any numeric type + var ( + anInt int = 'h' + anInt8 int8 = 'h' + anInt16 int16 = 'h' + anInt32 int32 = 'h' + + // rune literal's default type is: rune + // so, you don't need to specify it. + // aRune rune = 'h' + aRune = 'h' + + // and so on... + ) + + fmt.Println() + fmt.Printf("rune literals are typeless:\n\t%T %T %T %T %T\n", + anInt, anInt8, anInt16, anInt32, aRune) + + fmt.Println() + + // all are the same rune + + // beginning with go 1.13 you can type: 0b0110_1000 instead + // fmt.Printf("%q as binary: %08[1]b\n", 0b0110_1000) + fmt.Printf("%q in decimal: %[1]d\n", 104) + fmt.Printf("%q in binary : %08[1]b\n", 'h') + fmt.Printf("%q in hex : 0x%[1]x\n", 0x68) +} diff --git a/19-strings-runes-bytes/02-bytes-runes-strings-charset-table/main.go b/19-strings-runes-bytes/02-bytes-runes-strings-charset-table/main.go new file mode 100644 index 0000000..c8dfcb8 --- /dev/null +++ b/19-strings-runes-bytes/02-bytes-runes-strings-charset-table/main.go @@ -0,0 +1,69 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "os" + "strconv" + "strings" +) + +func main() { + var start, stop int + + if args := os.Args[1:]; len(args) == 2 { + start, _ = strconv.Atoi(args[0]) + stop, _ = strconv.Atoi(args[1]) + } + + if start == 0 || stop == 0 { + start, stop = 'A', 'Z' + } + + fmt.Printf("%-10s %-10s %-10s %-12s\n%s\n", + "literal", "dec", "hex", "encoded", + strings.Repeat("-", 45)) + + for n := start; n <= stop; n++ { + fmt.Printf("%-10c %-10[1]d %-10[1]x % -12x\n", n, string(n)) + } +} + +/* +EXAMPLE UNICODE BLOCKS + +1 byte +------------------------------------------------------------ +asciiStart = '\u0001' -> 32 +asciiStop = '\u007f' -> 127 + +upperCaseStart = '\u0041' -> 65 +upperCaseStop = '\u005a' -> 90 + +lowerCaseStart = '\u0061' -> 97 +lowerCaseStop = '\u007a' -> 122 + + +2 bytes +------------------------------------------------------------ +latin1Start = '\u0080' -> 161 +latin1Stop = '\u00ff' -> 255 + + +3 bytes +------------------------------------------------------------ +dingbatStart = '\u2700' -> 9984 +dingbatStop = '\u27bf' -> 10175 + + +4 bytes +------------------------------------------------------------ +emojiStart = '\U0001f600' -> 128512 +emojiStop = '\U0001f64f' -> 128591 +*/ diff --git a/19-strings-runes-bytes/03-bytes-runes-strings-examples/main.go b/19-strings-runes-bytes/03-bytes-runes-strings-examples/main.go new file mode 100644 index 0000000..a028613 --- /dev/null +++ b/19-strings-runes-bytes/03-bytes-runes-strings-examples/main.go @@ -0,0 +1,72 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "unicode/utf8" + "unsafe" +) + +func main() { + str := "Yūgen ☯ 💀" + + // can't change a string + // a string is a read-only byte-slice + // str[0] = 'N' + // str[1] = 'o' + + bytes := []byte(str) + + // can change a byte slice + // bytes[0] = 'N' + // bytes[1] = 'o' + + str = string(bytes) + + fmt.Printf("%s\n", str) + fmt.Printf("\t%d bytes\n", len(str)) + fmt.Printf("\t%d runes\n", utf8.RuneCountInString(str)) + fmt.Printf("% x\n", bytes) + fmt.Printf("\t%d bytes\n", len(bytes)) + fmt.Printf("\t%d runes\n", utf8.RuneCount(bytes)) + + // fmt.Println() + // for i, r := range str { + // fmt.Printf("str[%2d] = % -12x = %q\n", i, string(r), r) + // } + + fmt.Println() + fmt.Printf("1st byte : %c\n", str[0]) // ok + fmt.Printf("2nd byte : %c\n", str[1]) // not ok + fmt.Printf("2nd rune : %s\n", str[1:3]) // ok + fmt.Printf("last rune : %s\n", str[len(str)-4:]) // ok + + // disadvantage: each one is 4 bytes + runes := []rune(str) + + fmt.Println() + fmt.Printf("%s\n", str) + fmt.Printf("\t%d bytes\n", int(unsafe.Sizeof(runes[0]))*len(runes)) + fmt.Printf("\t%d runes\n", len(runes)) + + fmt.Printf("1st rune : %c\n", runes[0]) + fmt.Printf("2nd rune : %c\n", runes[1]) + fmt.Printf("first five : %c\n", runes[:5]) + + fmt.Println() + + word := "öykü" + fmt.Printf("%q in runes: %c\n", word, []rune(word)) + fmt.Printf("%q in bytes: % [1]x\n", word) + + fmt.Printf("%s %s\n", word[:2], []byte{word[0], word[1]}) // ö + fmt.Printf("%c\n", word[2]) // y + fmt.Printf("%c\n", word[3]) // k + fmt.Printf("%s %s\n", word[4:], []byte{word[4], word[5]}) // ü +} diff --git a/19-strings-runes-bytes/04-rune-decoding/01/main.go b/19-strings-runes-bytes/04-rune-decoding/01/main.go new file mode 100644 index 0000000..4ee22f3 --- /dev/null +++ b/19-strings-runes-bytes/04-rune-decoding/01/main.go @@ -0,0 +1,46 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "unicode/utf8" +) + +func main() { + const text = `Galaksinin Batı Sarmal Kolu'nun bir ucunda, haritası bile çıkarılmamış ücra bir köşede, gözlerden uzak, küçük ve sarı bir güneş vardır. + +Bu güneşin yörüngesinde, kabaca yüz kırksekiz milyon kilometre uzağında, tamamıyla önemsiz ve mavi-yeşil renkli, küçük bir gezegen döner. + +Gezegenin maymun soyundan gelen canlıları öyle ilkeldir ki dijital kol saatinin hâlâ çok etkileyici bir buluş olduğunu düşünürler.` + + r, size := utf8.DecodeRuneInString("öykü") + fmt.Printf("rune: %c size: %d bytes.\n", r, size) + + r, size = utf8.DecodeRuneInString("ykü") + fmt.Printf("rune: %c size: %d bytes.\n", r, size) + + r, size = utf8.DecodeRuneInString("kü") + fmt.Printf("rune: %c size: %d bytes.\n", r, size) + + r, size = utf8.DecodeRuneInString("ü") + fmt.Printf("rune: %c size: %d bytes.\n", r, size) + + // for range loop automatically decodes the runes + // but it gives you the position of the current rune + // instead of its size. + + // for _, r := range text {} + for i := 0; i < len(text); { + r, size := utf8.DecodeRuneInString(text[i:]) + fmt.Printf("%c", r) + + i += size + } + fmt.Println() +} diff --git a/19-strings-runes-bytes/04-rune-decoding/02/benchmarks/main.go b/19-strings-runes-bytes/04-rune-decoding/02/benchmarks/main.go new file mode 100644 index 0000000..cd13fed --- /dev/null +++ b/19-strings-runes-bytes/04-rune-decoding/02/benchmarks/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "bytes" + "fmt" + "testing" + "unicode" + "unicode/utf8" +) + +/* +Let's run this program to see the speed differences. + +benchDecoder 30000000 46.2 ns/op --> BEST +benchForRange 30000000 53.0 ns/op --> MEDIOCRE +benchConcater 20000000 93.7 ns/op --> WORST +*/ + +var word = []byte("öykü") + +func decoder(w []byte) { + _, size := utf8.DecodeRune(word) + copy(w[:size], bytes.ToUpper(w[:size])) +} + +func forRange(w []byte) { + var size int + for i := range string(w) { + if i > 0 { + size = i + break + } + } + copy(w[:size], bytes.ToUpper(w[:size])) +} + +var globalString string + +func concater(w []byte) { + runes := []rune(string(w)) + runes[0] = unicode.ToUpper(runes[0]) + globalString = string(runes) +} + +func bench(technique func([]byte)) testing.BenchmarkResult { + return testing.Benchmark(func(b *testing.B) { + for i := 0; i < b.N; i++ { + technique(word) + } + }) +} + +func main() { + fmt.Println("benchDecoder", bench(decoder)) + fmt.Println("benchForRange", bench(forRange)) + fmt.Println("benchConcater", bench(concater)) +} diff --git a/19-strings-runes-bytes/04-rune-decoding/02/main.go b/19-strings-runes-bytes/04-rune-decoding/02/main.go new file mode 100644 index 0000000..3dd74d0 --- /dev/null +++ b/19-strings-runes-bytes/04-rune-decoding/02/main.go @@ -0,0 +1,43 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "bytes" + "fmt" + "unicode/utf8" +) + +func main() { + word := []byte("öykü") + fmt.Printf("%s = % [1]x\n", word) + + // how to make the first rune uppercase? + + // you need to find the starting and ending position of the first rune + + // 1st way: `for range` + // you can't get the runes by range overing a byte slice + // first, you need to convert it to a string + var size int + for i := range string(word) { + if i > 0 { + size = i + break + } + } + + // 2nd way: let's do it using the utf8 package's DecodeRune function + _, size = utf8.DecodeRune(word) + + // overwrite the current bytes with the new uppercased bytes + copy(word[:size], bytes.ToUpper(word[:size])) + + // to get printed bytes/runes need to be encoded in a string + fmt.Printf("%s = % [1]x\n", word) +} diff --git a/19-strings-runes-bytes/05-internals/main.go b/19-strings-runes-bytes/05-internals/main.go new file mode 100644 index 0000000..e5cfce7 --- /dev/null +++ b/19-strings-runes-bytes/05-internals/main.go @@ -0,0 +1,45 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + // empty := "" + // dump(empty) + + hello := "hello" + dump(hello) + dump("hello") + dump("hello!") + + for i := range hello { + dump(hello[i : i+1]) + } + + dump(string([]byte(hello))) + dump(string([]byte(hello))) + dump(string([]rune(hello))) +} + +// StringHeader is used by a string value +// In practice, you should use: reflect.Header +type StringHeader struct { + // points to a backing array's item + pointer uintptr // where it starts + length int // where it ends +} + +// dump prints the string header of a string value +func dump(s string) { + ptr := *(*StringHeader)(unsafe.Pointer(&s)) + fmt.Printf("%q: %+v\n", s, ptr) +} diff --git a/20-project-spam-masker/01-step-1/main.go b/20-project-spam-masker/01-step-1/main.go new file mode 100644 index 0000000..a7fb9d2 --- /dev/null +++ b/20-project-spam-masker/01-step-1/main.go @@ -0,0 +1,54 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +/* +✅ #1- Get and check the input +✅ #2- Create a byte buffer and use it as the output +✅ #3- Write input to the buffer as it is and print it +✅ #4- Detect the link +#5- Mask the link +#6- Stop masking when whitespace is detected +#7- Put a http:// prefix in front of the masked link +*/ + +package main + +import ( + "fmt" + "os" +) + +func main() { + args := os.Args[1:] + if len(args) != 1 { + fmt.Println("gimme somethin' to mask!") + return + } + + const ( + link = "http://" + nlink = len(link) + ) + + var ( + text = args[0] + size = len(text) + buf = make([]byte, 0, size) + ) + + for i := 0; i < size; i++ { + buf = append(buf, text[i]) + + // slice the input and look for the link pattern + // do not slice it when it goes beyond the input text's capacity + if len(text[i:]) >= nlink && text[i:i+nlink] == link { + } + } + + // print out the buffer as text (string) + fmt.Println(string(buf)) +} diff --git a/20-project-spam-masker/01-step-1/spam.txt b/20-project-spam-masker/01-step-1/spam.txt new file mode 100644 index 0000000..6ad043d --- /dev/null +++ b/20-project-spam-masker/01-step-1/spam.txt @@ -0,0 +1,7 @@ +Hi guys, + +Here is my new spammy webpage http://www.mysuperpage.com <-- This is my website! + +Please click on the link now!!! + +When you click, I will be rich, thanks! \ No newline at end of file diff --git a/20-project-spam-masker/02-step-2/main.go b/20-project-spam-masker/02-step-2/main.go new file mode 100644 index 0000000..c0bfcc2 --- /dev/null +++ b/20-project-spam-masker/02-step-2/main.go @@ -0,0 +1,80 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +/* +✅ #1- Get and check the input +✅ #2- Create a byte buffer and use it as the output +✅ #3- Write input to the buffer as it is and print it +✅ #4- Detect the link +✅ #5- Mask the link +✅ #6- Stop masking when whitespace is detected +✅ #7- Put a http:// prefix in front of the masked link +*/ + +package main + +import ( + "fmt" + "os" +) + +const ( + link = "http://" + nlink = len(link) + mask = '*' +) + +func main() { + args := os.Args[1:] + if len(args) != 1 { + fmt.Println("gimme somethin' to mask!") + return + } + + var ( + text = args[0] + size = len(text) + buf = make([]byte, 0, size) + + in bool + ) + + for i := 0; i < size; i++ { + // slice the input and look for the link pattern + // do not slice it when it goes beyond the input text's capacity + if len(text[i:]) >= nlink && text[i:i+nlink] == link { + // set the flag: we're in a link! -> "http://....." + in = true + + // add the "http://" manually + buf = append(buf, link...) + + // jump to the next character after "http://" + i += nlink + } + + // get the current byte from the input + c := text[i] + + // disable the link detection flag + // this will prevent masking the rest of the bytes + switch c { + case ' ', '\t', '\n': // try -> unicode.IsSpace + in = false + } + + // if we're in the link detection mode (inside the link bytes) + // then, mask the current character + if in { + c = mask + } + buf = append(buf, c) + } + + // print out the buffer as text (string) + fmt.Println(string(buf)) +} diff --git a/20-project-spam-masker/02-step-2/spam.txt b/20-project-spam-masker/02-step-2/spam.txt new file mode 100644 index 0000000..6ad043d --- /dev/null +++ b/20-project-spam-masker/02-step-2/spam.txt @@ -0,0 +1,7 @@ +Hi guys, + +Here is my new spammy webpage http://www.mysuperpage.com <-- This is my website! + +Please click on the link now!!! + +When you click, I will be rich, thanks! \ No newline at end of file diff --git a/20-project-spam-masker/03-step-2-no-append/main.go b/20-project-spam-masker/03-step-2-no-append/main.go new file mode 100644 index 0000000..5d0fb2f --- /dev/null +++ b/20-project-spam-masker/03-step-2-no-append/main.go @@ -0,0 +1,68 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +/* +✅ #1- Get and check the input +✅ #2- Create a byte buffer and use it as the output +✅ #3- Write input to the buffer as it is and print it +✅ #4- Detect the link +✅ #5- Mask the link +✅ #6- Stop masking when whitespace is detected +✅ #7- Put a http:// prefix in front of the masked link +*/ + +package main + +import ( + "fmt" + "os" +) + +const ( + link = "http://" + nlink = len(link) + mask = '*' +) + +func main() { + args := os.Args[1:] + if len(args) != 1 { + fmt.Println("gimme somethin' to mask!") + return + } + + var ( + text = args[0] + size = len(text) + + // create a byte buffer directly from the string (text) + buf = []byte(text) + + in bool + ) + + for i := 0; i < size; i++ { + // no need to add an artificial http:// prefix + // it's already there + if len(text[i:]) >= nlink && text[i:i+nlink] == link { + in = true + i += nlink + } + + switch text[i] { + case ' ', '\t', '\n': // try -> unicode.IsSpace + in = false + } + + // when censoring mode: on + // directly manipulate the bytes on the buffer + if in { + buf[i] = mask + } + } + fmt.Println(string(buf)) +} diff --git a/20-project-spam-masker/03-step-2-no-append/spam.txt b/20-project-spam-masker/03-step-2-no-append/spam.txt new file mode 100644 index 0000000..6ad043d --- /dev/null +++ b/20-project-spam-masker/03-step-2-no-append/spam.txt @@ -0,0 +1,7 @@ +Hi guys, + +Here is my new spammy webpage http://www.mysuperpage.com <-- This is my website! + +Please click on the link now!!! + +When you click, I will be rich, thanks! \ No newline at end of file diff --git a/20-project-spam-masker/README.md b/20-project-spam-masker/README.md new file mode 100644 index 0000000..328bdbf --- /dev/null +++ b/20-project-spam-masker/README.md @@ -0,0 +1,60 @@ +# Spam Masker Challenge Tips + +## Rules: + +* You shouldn't use a standard library function. + +* You should only solve the challenge by manipulating the bytes directly. + +* Manipulate the bytes of a string using indexing, slicing, appending etc. + +* Be efficient: Do not use string concat (+ operator). + * Instead, create a new byte slice as a buffer from the given string argument. + * Then, manipulate it during your program. + * And, for once, print that buffer. + +* Mask only links starting with `http://` + +* Don't check for uppercase/lowercase letters + + * The goal is learning how to manipulate bytes in strings, it's not about creating the perfect masker. + + * For example: A spammer can prevent the masker like this (for now this is OK): + + ``` + "Here's my spammy page: hTTp://youth-elixir.com" + ^^ + ``` + +* But, you should catch this: + + ``` + INPUT: + Here's my spammy page: http://hehefouls.netHAHAHA see you. + + OUTPUT: + Here's my spammy page: http://******************* see you. + ``` + +## Steps: + +1. Check whether there's a command line argument or not. If not, quit from the program with a message. + +2. Create a byte buffer as big as the argument. + +3. Loop and detect the `http://` patterns + +4. Copy the input character by character to the buffer + +5. If you detect `http://` pattern, copy the `http://` first, then copy the `*`s instead of the original link until you see whitespace character. + + For example: + ``` + INPUT: + Here: http://www.mylink.com Click! + + OUTPUT: + Here: http://************** Click! + ``` + +6. Print the buffer as a string \ No newline at end of file diff --git a/21-project-text-wrapper/README.md b/21-project-text-wrapper/README.md new file mode 100644 index 0000000..9790dab --- /dev/null +++ b/21-project-text-wrapper/README.md @@ -0,0 +1,18 @@ +# Text Wrapper Challenge Guideline + +In this project your goal is to mimic the soft text wrapping feature of text editors. For example, when there are 100 characters on a line and if the soft-wrapping is set to 40, the editors cut each line that goes beyond 40 characters and display the rest of the line in the next line instead. + +## EXAMPLE + +Wrap the text for 40 characters in a line. For example, for the following input, the program should print the following output. + +**INPUT:** +Hello world, how is it going? It is ok. The weather is beautiful. + +**OUTPUT:** +Hello world, how is it going? It is ok. +The weather is beautiful. + +## RULES + +* The program should also work with Unicode text. You can find a unicode story in [story.txt](story.txt) file in the current folder. Please use the text in the file and soft-wrap it to 40 characters. \ No newline at end of file diff --git a/21-project-text-wrapper/main.go b/21-project-text-wrapper/main.go new file mode 100644 index 0000000..735182b --- /dev/null +++ b/21-project-text-wrapper/main.go @@ -0,0 +1,38 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "unicode" +) + +func main() { + const text = `Galaksinin Batı Sarmal Kolu'nun bir ucunda, haritası bile çıkarılmamış ücra bir köşede, gözlerden uzak, küçük ve sarı bir güneş vardır. + +Bu güneşin yörüngesinde, kabaca yüz kırksekiz milyon kilometre uzağında, tamamıyla önemsiz ve mavi-yeşil renkli, küçük bir gezegen döner. + +Gezegenin maymun soyundan gelen canlıları öyle ilkeldir ki dijital kol saatinin hâlâ çok etkileyici bir buluş olduğunu düşünürler.` + + const maxWidth = 40 + + var lw int // line width + + for _, r := range text { + fmt.Printf("%c", r) + + switch lw++; { + case lw > maxWidth && r != '\n' && unicode.IsSpace(r): + fmt.Println() + fallthrough + case r == '\n': + lw = 0 + } + } + fmt.Println() +} diff --git a/21-project-text-wrapper/story.txt b/21-project-text-wrapper/story.txt new file mode 100644 index 0000000..b8784a4 --- /dev/null +++ b/21-project-text-wrapper/story.txt @@ -0,0 +1,5 @@ +Galaksinin Batı Sarmal Kolu'nun bir ucunda, haritası bile çıkarılmamış ücra bir köşede, gözlerden uzak, küçük ve sarı bir güneş vardır. + +Bu güneşin yörüngesinde, kabaca yüz kırksekiz milyon kilometre uzağında, tamamıyla önemsiz ve mavi-yeşil renkli, küçük bir gezegen döner. + +Gezegenin maymun soyundan gelen canlıları öyle ilkeldir ki dijital kol saatinin hâlâ çok etkileyici bir buluş olduğunu düşünürler. \ No newline at end of file