One place for hosting & domains

      pacote

      Uma introdução ao pacote de Strings em Go


      Introdução

      O pacote stringem Go possui várias funções disponíveis para trabalhar com o string data type​​​. Essas funções nos permitem modificar e manipular strings facilmente. Podemos pensar em funções como ações que realizamos nos elementos do nosso código. Funções integradas são aquelas definidas na linguagem de programação Go e ficam prontamente disponíveis para o nosso uso.

      Neste tutorial, avaliaremos várias funções diferentes que podemos usar para trabalhar com as strings em Go.

      Criando strings em letras maiúsculas e minúsculas

      As funções strings.ToUpper e strings.ToLower vão retornar uma string com todas as letras de uma string original convertidas em letras maiúsculas ou minúsculas. Como as strings são tipos de dados imutáveis, a string retornada será uma nova string. Quaisquer caracteres na string que não sejam letras não serão alterados.

      Para converter a string "Sammy Shark" para ser toda em maiúscula, use a função strings.ToUpper:

      ss := "Sammy Shark"
      fmt.Println(strings.ToUpper(ss))
      

      Output

      SAMMY SHARK

      Para converter para minúsculas:

      fmt.Println(strings.ToLower(ss))
      

      Output

      sammy shark

      Como você está usando o pacote strings, primeiro você precisa importá-lo para um programa. Para converter a string em maiúsculas e minúsculas, o programa todo ficaria da seguinte forma:

      package main
      
      import (
          "fmt"
          "strings"
      )
      
      func main() {
          ss := "Sammy Shark"
          fmt.Println(strings.ToUpper(ss))
          fmt.Println(strings.ToLower(ss))
      }
      

      As funções strings.ToUpper e strings.ToLower facilitam a avaliação e comparação de strings, tornando consistente o uso de maiúsculas e minúsculas de ponta a ponta. Por exemplo, se um usuário escreve seu nome com todas as letras minúsculas, ainda assim conseguiremos saber se o nome dele está em nosso banco de dados, comparando-o com uma versão com todas as letras maiúsculas.

      Funções de busca de string

      O pacote strings tem uma série de funções que ajudam a determinar se uma string contém uma sequência específica de caracteres.

      Função Uso
      strings.HasPrefix Procura a string desde o início
      strings.HasSuffix Procura a string a partir do final
      strings.Contains Procura em qualquer lugar na string
      strings.Count Conta quantas vezes a string aparece

      As funções strings.HasPrefix e strings.HasSuffix permitem que você verifique se uma string começa ou termina com um conjunto específico de caracteres.

      Por exemplo, para verificar se a string "Sammy Shark" começa com Sammy e termina com o Shark:

      ss := "Sammy Shark"
      fmt.Println(strings.HasPrefix(ss, "Sammy"))
      fmt.Println(strings.HasSuffix(ss, "Shark"))
      

      Output

      true true

      Você usaria a função strings.Contains para verificar se "Sammy Shark"contém a sequência Sh:

      fmt.Println(strings.Contains(ss, "Sh"))
      

      Output

      true

      Por fim, para ver quantas vezes a letra S aparece na frase Sammy Shark:

      fmt.Println(strings.Count(ss, "S"))
      

      Output

      2

      Nota: todas as strings em Go diferenciam maiúsculas de minúsculas. Isso significa que Sammy não é o mesmo que sammy.

      Usar um s minúsculo para obter uma contagem do Sammy Shark não é o mesmo que usar o S maiúsculo:

      fmt.Println(strings.Count(ss, "s"))
      

      Output

      0

      Como S é diferente de s, a contagem será 0.

      As funções de string são úteis quando você quer comparar ou pesquisar strings no seu programa.

      Determinando o tamanho da string

      A função integrada len() retorna o número de caracteres em uma string. Essa função é útil para quando você tiver que impor o tamanho mínimo ou máximo de uma senha, por exemplo, ou para truncar strings maiores para que fiquem dentro de certos limites para usar como abreviações.

      Para demonstrar essa função, encontraremos o tamanho de uma string longa:

      import (
          "fmt"
          "strings"
      )
      
      func main() {
              openSource := "Sammy contributes to open source."
              fmt.Println(len(openSource))
      }
      

      Output

      33

      Definimos a variável openSource igual a string "Sammy contributes to open source." e então passamos essa variável para a função len() com len(openSource). Por fim, movemos a função para que faça parte da função fmt.Println() para que pudéssemos ver o resultado do programa na tela.

      Lembre-se de que a função len() contará qualquer caractere vinculado por aspas duplas – incluindo letras, números, espaços em branco e símbolos.

      Funções para a manipulação de string

      As funções strings.Join, strings.Split e strings.ReplaceAll são algumas maneiras adicionais de manipular as strings em Go.

      A função strings.Join é útil para combinar uma fração das strings em uma única e nova string.

      Para criar uma string separada por vírgulas de uma fatia de strings, usaríamos essa função de acordo com o seguinte:

      fmt.Println(strings.Join([]string{"sharks", "crustaceans", "plankton"}, ","))
      

      Output

      sharks,crustaceans,plankton

      Se quisermos adicionar uma vírgula e um espaço entre os valores da string na nossa nova string, podemos simplesmente reescrever nossa expressão com um espaço em branco após a vírgula: strings.Join([]string{"sharks", "crustaceans", "plankton"}, ", ").

      Assim como podemos juntar strings, também podemos dividir as strings. Para fazer isso, podemos usar a função strings.Split e dividir nos espaços:

      balloon := "Sammy has a balloon."
      s := strings.Split(balloon, " ")
      fmt.Println(s)
      

      Output

      [Sammy has a balloon]

      O resultado é uma fatia de strings. Como a função strings.Println foi usada, é difícil distinguir o que seja o resultando apenas olhando para ele. Para verificar que se trata, de fato, de strings, utilize a função fmt.Printf com o verbo %q para citar as strings:

      fmt.Printf("%q", s)
      

      Output

      ["Sammy" "has" "a" "balloon."]

      Outra função útil, além de strings.Split é a strings.Fields. A diferença é que a função strings.Fields irá ignorar todos os espaços em branco e irá dividir apenas os fields em questão em uma string:

      data := "  username password     email  date"
      fields := strings.Fields(data)
      fmt.Printf("%q", fields)
      

      Output

      ["username" "password" "email" "date"]

      A função strings.ReplaceAll pode tomar uma string original e retornar uma string atualizada com algumas substituições.

      Digamos que o balão de Sammy esteja perdido. Como Sammy já não tem esse balão, mudaríamos a substring "has"(tem) da string original balloon (balão) para "had" (tinha) em uma nova string:

      fmt.Println(strings.ReplaceAll(balloon, "has", "had"))
      

      Dentro dos parênteses, primeiro está a variável balloon que armazena a string original; a segunda substring "has" é a que gostaríamos de substituir e a terceira substring "had" é o que iria substituir a segunda substring. Nosso resultado ficaria parecida com a seguinte, depois que incorporarmos isto em um um programa:

      Output

      Sammy had a balloon.

      Usar as funções string strings.Join, strings.Split e strings.ReplaceAll fornecerá a você um maior controle para manipular strings em Go.

      Conclusão

      Este tutorial examinou algumas das funções comuns do pacote string quanto ao tipo de dados de string que você pode usar para trabalhar e manipular strings em seus programas em Go.

      Você pode aprender mais sobre outros tipos de dados em Entendendo os tipos de dados e ler mais sobre strings em Uma introdução para trabalhar com strings.



      Source link

      Como usar o pacote Flag em Go


      Introdução

      Os utilitários de linha de comando raramente são úteis, pois não vêm prontos para usar e necessitam de configuração adicional. Padrões bons são importantes, mas os utilitários úteis precisam aceitar a configuração dos usuários. Na maioria das plataformas, os utilitários de linha de comando aceitam sinalizadores (flags) que permitem personalizar a execução do comando. Os sinalizadores são strings de valores-chave delimitados, adicionadas após o nome do comando. A linguagem Go permite que você crie utilitários de linha de comando que aceitam sinalizadores, usando o pacote flag da biblioteca padrão.

      Neste tutorial, serão explorados várias maneiras de usar o pacote flag para construir diferentes tipos de utilitários de linha de comando. Você usará um sinalizador para controlar o resultado do programa, introduzir argumentos posicionais – nos quais você mistura sinalizadores e outros dados e, em seguida, implementar subcomandos.

      Usando um sinalizador para alterar o comportamento de um programa

      Usar o pacote flag envolve três passos: primeiro, defina as variáveis para capturar os valores de sinalizador; em seguida, defina os sinalizadores que o seu aplicativo em Go usará e, por fim, analise os sinalizadores fornecidos para o aplicativo na execução. A maioria das funções dentro do pacote flag diz respeito à definição dos sinalizadores e de conectá-los às variáveis que você tiver definido. A fase de análise é manipulada pela função Parse().

      Para ilustrar, você criará um programa que define um sinalizador Booleano, o qual altera a mensagem que será impressa em um resultado padrão. Se houver um sinalizador -color fornecido, o programa irá imprimir uma mensagem em azul. Se nenhum sinalizador for fornecido, a mensagem será impressa sem qualquer cor.

      Crie um novo arquivo chamado boolean.go:

      Adicione o código a seguir ao arquivo para criar o programa:

      boolean.go

      package main
      
      import (
          "flag"
          "fmt"
      )
      
      type Color string
      
      const (
          ColorBlack  Color = "u001b[30m"
          ColorRed          = "u001b[31m"
          ColorGreen        = "u001b[32m"
          ColorYellow       = "u001b[33m"
          ColorBlue         = "u001b[34m"
          ColorReset        = "u001b[0m"
      )
      
      func colorize(color Color, message string) {
          fmt.Println(string(color), message, string(ColorReset))
      }
      
      func main() {
          useColor := flag.Bool("color", false, "display colorized output")
          flag.Parse()
      
          if *useColor {
              colorize(ColorBlue, "Hello, DigitalOcean!")
              return
          }
          fmt.Println("Hello, DigitalOcean!")
      }
      

      Esse exemplo usa Sequências de Escape ANSI para instruir o terminal a exibir um resultado colorido. Essas são sequências especializadas de caracteres, de modo que faz sentido definir um novo tipo para elas. Nesse exemplo, chamamos àquele tipo de Color e definimos o tipo como uma string. Então, definimos uma paleta de cores para usar no bloco const que segue. A função colorize, definida após o bloco const, aceita uma dessas constantes Color e uma variável string para que a mensagem seja colorida. Depois, a função diz ao terminal para alterar a cor, imprimindo primeiro a sequência de escape da cor solicitada; em seguida, imprime a mensagem e, por fim, solicita que o terminal redefina sua cor, imprimindo a sequência especial para redefinição de cores.

      Dentro do main, usamos a função flag.Bool para definir um sinalizador booleano chamado color. O segundo parâmetro para esta função, false, define o valor padrão para esse sinalizador, quando não for fornecido. Ao contrário das expectativas que você possa ter, definir o parâmetro como true não reverte o comportamento na medida em que o fornecimento de um sinalizador fará com que um parâmetro se torne falso. Consequentemente, o valor desse parâmetro é quase sempre false com os sinalizadores booleanos.

      O parâmetro final é uma string de documentação que pode ser impressa como uma mensagem de uso. O valor retornado dessa função é um ponteiro para um bool. A função flag.Parse, na linha seguinte, usa esse ponteiro para definir a variável bool com base nos sinalizadores transmitidos pelo usuário. Depois disso, conseguiremos verificar o valor desse ponteiro bool, desreferenciando o ponteiro. No tutorial sobre ponteiros, você encontra mais informações sobre variáveis de ponteiros. Usando esse valor Booleano, podemos então chamar colorize quando um sinalizador -color estiver definido e chamar a variável fmt.Println quando o sinalizador estiver ausente.

      Salve o arquivo e execute o programa sem nenhum sinalizador:

      Você verá o seguinte resultado:

      Output

      Hello, DigitalOcean!

      Agora, execute este programa novamente com o sinalizador -color:

      O resultado será o mesmo texto, mas, dessa vez, na cor azul.

      Os sinalizadores não são os únicos valores enviados para os comandos. Você também pode enviar nomes de arquivo ou outros dados.

      Normalmente, os comandos receberão uma série de argumentos que agem como o assunto que o comando tem em foco. Por exemplo, o comando head - que imprime as primeiras linhas de um arquivo - com frequência, é invocado como head example.txt. O arquivo example.txt é um argumento posicional na invocação do comando head.

      A função Parse() continuará a analisar os sinalizadores que encontrar, até detectar um argumento não sinalizador. O pacote flag os disponibiliza através das funções Args() e Arg().

      Para demonstrar isso, você compilará uma reimplementação simplificada do comando head, o qual exibe várias das primeiras linhas de um determinado arquivo:

      Crie um novo arquivo chamado head.go e adicione o seguinte código:

      head.go

      package main
      
      import (
          "bufio"
          "flag"
          "fmt"
          "io"
          "os"
      )
      
      func main() {
          var count int
          flag.IntVar(&count, "n", 5, "number of lines to read from the file")
          flag.Parse()
      
          var in io.Reader
          if filename := flag.Arg(0); filename != "" {
              f, err := os.Open(filename)
              if err != nil {
                  fmt.Println("error opening file: err:", err)
                  os.Exit(1)
              }
              defer f.Close()
      
              in = f
          } else {
              in = os.Stdin
          }
      
          buf := bufio.NewScanner(in)
      
          for i := 0; i < count; i++ {
              if !buf.Scan() {
                  break
              }
              fmt.Println(buf.Text())
          }
      
          if err := buf.Err(); err != nil {
              fmt.Fprintln(os.Stderr, "error reading: err:", err)
          }
      }
      

      Primeiro, definimos uma variável count para reter o número de linhas que o programa deverá ler do arquivo. Então, definimos um sinalizador -n, usando a flag.IntVar e espelhando o comportamento do programa original head. Essa função nos permite enviar nosso próprio ponteiro para uma variável, em contraste com as funções flag que não têm o sufixo Var. Além dessa diferença, os demais parâmetros para a flag.IntVar seguem sua contraparte flag.Int: o nome do sinalizador, um valor padrão e uma descrição. Como no exemplo anterior, chamamos o flag.Parse() para processar a entrada do usuário.

      A próxima seção lê o arquivo. Primeiro, definimos uma variável io.Reader que será definida para o arquivo solicitado pelo usuário, ou para a entrada padrão enviada para o programa. Dentro da instrução if, usamos a função flag.Arg para acessar o primeiro argumento posicional depois de todos os sinalizadores. Se o usuário forneceu um nome de arquivo, este estará definido. Caso contrário, será a string vazia (""). Quando um nome de arquivo estiver presente, usamos a função os.Open para abrir aquele arquivo e definir o io.Reader que definimos anteriormente para aquele arquivo. Caso contrário, usamos o os.Stdin para ler a partir da entrada padrão.

      A seção final usa um *bufio.Scanner, criado com o bufio.NewScanner para ler linhas da entrada in da variável io.Reader. Iteramos até o valor da count usando um loop for, que chamará o break caso a verificação da linha com o buf.Scan produza um valor false, indicando que o número de linhas é menor do que o número solicitado pelo usuário.

      Execute este programa e exiba o conteúdo do arquivo que acabou de escrever, usando o head.go como o argumento do arquivo:

      • go run head.go -- head.go

      O separador -- é um sinalizador especial reconhecido pelo pacote flag, o qual indica que não haverá mais argumentos de sinalização na sequência. Quando executar este comando, você recebe o seguinte resultado:

      Output

      package main import ( "bufio" "flag"

      Use o sinalizador -n que você definiu para ajustar a quantidade de resultado:

      • go run head.go -n 1 head.go

      Isso gera apenas a instrução do pacote:

      Output

      package main

      Por fim, quando o programa detecta que nenhum argumento posicional foi fornecido, ele lê a entrada das entradas padrão, assim como com o head. Tente executar este comando:

      • echo "fishnlobstersnsharksnminnows" | go run head.go -n 3

      Você verá o resultado:

      Output

      fish lobsters sharks

      O comportamento das funções flag que você viu até agora tem sido limitado a examinar a invocação do comando inteiro. Nem sempre você vai desejar esse comportamento, especialmente se estiver escrevendo uma ferramenta de linha de comando que suporte subcomandos.

      Usando o flagSet para implementar subcomandos

      Os aplicativos de linha de comando modernos com frequência implementam “subcomandos” para agrupar um conjunto de ferramentas sob um único comando. A ferramenta mais conhecida que usa esse padrão é a git. Ao examinar um comando como o git init, o git é o comando e o init é o subcomando do git. Uma característica notável dos subcomandos é que cada subcomando pode ter sua própria coleção de sinalizadores.

      Os aplicativos em Go podem ser oferecer suporte aos subcomandos com seu próprio conjunto de sinalizadores, usando o tipo flag.( *FlagSet). Para demonstrar isso, crie um programa que implementa um comando usando dois subcomandos com sinalizadores diferentes.

      Crie um novo arquivo chamado subcommand.go e adicione o seguinte conteúdo ao arquivo:

      package main
      
      import (
          "errors"
          "flag"
          "fmt"
          "os"
      )
      
      func NewGreetCommand() *GreetCommand {
          gc := &GreetCommand{
              fs: flag.NewFlagSet("greet", flag.ContinueOnError),
          }
      
          gc.fs.StringVar(&gc.name, "name", "World", "name of the person to be greeted")
      
          return gc
      }
      
      type GreetCommand struct {
          fs *flag.FlagSet
      
          name string
      }
      
      func (g *GreetCommand) Name() string {
          return g.fs.Name()
      }
      
      func (g *GreetCommand) Init(args []string) error {
          return g.fs.Parse(args)
      }
      
      func (g *GreetCommand) Run() error {
          fmt.Println("Hello", g.name, "!")
          return nil
      }
      
      type Runner interface {
          Init([]string) error
          Run() error
          Name() string
      }
      
      func root(args []string) error {
          if len(args) < 1 {
              return errors.New("You must pass a sub-command")
          }
      
          cmds := []Runner{
              NewGreetCommand(),
          }
      
          subcommand := os.Args[1]
      
          for _, cmd := range cmds {
              if cmd.Name() == subcommand {
                  cmd.Init(os.Args[2:])
                  return cmd.Run()
              }
          }
      
          return fmt.Errorf("Unknown subcommand: %s", subcommand)
      }
      
      func main() {
          if err := root(os.Args[1:]); err != nil {
              fmt.Println(err)
              os.Exit(1)
          }
      }
      

      Esse programa está dividido em algumas partes: a função main, a função root e as funções individuais para implementar o subcomando. A função main manipula os erros retornados dos comandos. Caso alguma função retorne um erro, a instrução if irá capturá-lo, imprimi-lo e o programa fechará com um código de status de 1, indicando ao resto do sistema operacional que um erro ocorreu. Dentro do main, enviamos todos os argumentos com os quais o programa foi invocado para o root. Removemos o primeiro argumento, que é o nome do programa (nos exemplos anteriores ./subcommand) dividindo o os.Args primeiro.

      A função root define o []Runner, onde todos os subcomandos seriam definidos. Runner é uma interface para subcomandos que permite à root recuperar o nome do subcomando usando o Name() e compará-lo ao conteúdo da variável subcommand. Assim que o subcomando correto for localizado, após a iteração através da variável cmds, inicializamos o subcomando com o resto dos argumentos e invocamos o método Run () do comando.

      Definimos apenas um subcomando, embora esse framework nos permita facilmente criar outros. O GreetCommand é instanciado usando o NewGreetCommand, onde criamos um novo *flag.FlagSet usando o flag.NewFlagSet. O flag.NewFlagSet recebe dois argumentos: um nome para o conjunto de sinalizadores e uma estratégia para a comunicação dos erros de análise. O nome do *flag.FlagSet é acessível usando o método flag.( *FlagSet). Name. Usamos isso no método (*GreetCommand). Name() para que o nome do subcomando corresponda ao nome que demos ao *flag.FlagSet. O NewGreetCommand também define um sinalizador -name de maneira semelhando à dos exemplos anteriores, mas ele chama isso como um método fora do campo *flag.FlagSet do *GreetCommand, gc.fs. Quando a root chama o método Init() do *GreetCommand, enviamos os argumentos fornecidos para o método Parse do campo *flag.FlagSet.

      Será mais fácil ver os subcomandos se você compilar este programa e, em seguida, executá-lo. Compile o programa:

      Agora, execute o programa sem argumentos:

      Você verá este resultado:

      Output

      You must pass a sub-command

      Agora, execute o programa com o subcomando greet:

      Isso produz o seguinte resultado:

      Output

      Hello World !

      Agora, utilize o sinalizador -name com o greet para especificar um nome:

      • ./subcommand greet -name Sammy

      Você verá este resultado do programa:

      Output

      Hello Sammy !

      Esse exemplo ilustra alguns princípios por trás do fato de como aplicativos de linha de comando maiores poderiam ser estruturados em Go. Os FlagSets foram desenvolvidos para dar aos desenvolvedores mais controle sobre onde e como os sinalizadores são processadas pela lógica de análise de sinalizadores.

      Conclusão

      Os sinalizadores tornam seus aplicativos mais úteis em mais contextos porque dão aos usuários controle sobre como os programas são executados. É importante dar aos usuários padrões úteis, mas você também deve dar a eles a oportunidade de substituir as configurações que não funcionam para sua situação. Você viu que o pacote flag oferece escolhas flexíveis para apresentar opções de configuração aos seus usuários. Você pode escolher alguns sinalizadores simples ou compilar um conjunto expansível de subcomandos. Em qualquer caso, usar o pacote flag ajudará na compilação de utilitários no estilo da longa história de ferramentas de linha de comando flexíveis e com scripts.

      Para aprender mais sobre a linguagem de programação Go, confira nossa série completa de artigos sobre Como programar em Go.



      Source link