Ir ao conteúdo
  • Cadastre-se
kurt Rodrigues

C++ Problema para ler uma string com acento

Recommended Posts

Olá por favor estou com problema para ter ler um input de uma string com acento. Usando setlocale arrumei a saída, mas a entrada

ainda permanece com uma simbolo no lugar da letra acentuada. Alguém por gentileza poderia me indicar onde se encontra o erro. Grato

 

/*15) Faça um algoritmo e o fluxograma que leia o nome da capital do Brasil. Se a
resposta estiver correta, imprimir PARABÉNS, caso contrário, ERROU.
(Considerar: Brasília).*/

 

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>

main () {

setlocale (LC_ALL,"portuguese");

//----------------------------------------------------
char capital[9];
    printf("Digite qual é a capital do Brasil: ");
    fgets(capital,9,stdin); // limitar a string a 8 caractes + 1

//----------------------------------------------------
    if (strcmp(capital,"Brasília")==0) { // COMPARA DUAS STRINGS 

        printf("PARABÉNS");
    }

    else {
        printf("**INCORRETO**\n\n O digitado foi %s e o correto é Brasília\n",capital);
    }

}

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Brasília tem mais de oito caracteres, pois está codificada em UTF-8 quando é passada pelo terminal. A codificação UTF-8 é a mesma da ASCII para os caracteres que estão na tabela ASCII original, e para os que não estão, vai acrescentando bytes extras na frente do caractere.

 

A comparação com strcmp() irá sempre falhar pois fgets() limitou o tamanho da string a ser lida e acabou deixando-a truncada. Tente imprimir a string recebida pelo programa antes de comparar para ver o que está acontecendo.

Uma solução rápida para o problema seria acrescentar um caractere a mais para ser lido pela string. Observe a saída dos seguintes programas executados em um sistema com locale pt-BR.utf8:
 

echo a | xxd -b
00000000: 01100001 00001010                                      a.

 

echo í | xxd -b
00000000: 11000011 10101101 00001010                             ...


O primeiro comando retornou que os caracteres passados pelo terminal foram um 'a', que em binário vale 97 pela ASCII e um newline, que vale 10 em decimal. São dois bytes de informação e o próprio caractere ocupa somente um byte. Já no segundo, temos dois caracteres iniciando em 1, indicando que estão fora da ASCII clássica de 7 bits, seguidos de um newline. Isso significa que o caractere 'í' ocupa dois bytes, o dobro que um caractere comum ocuparia.

  • Curtir 2

Compartilhar este post


Link para o post
Compartilhar em outros sites

:rezando:

 

 

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Davi Silva Santos Algum tutorial para melhor compreensão recomendado?

Pelo pouco que sei, mudando o código de página do prompt para 1252 funciona bem, mas nunca compreendi muito bem essa questão. Só sei que usando o setlocale você somente consegue imprimir as literais corretamente:
Screenshot_2.jpg.6c9fcd4c59ac4f5b8a7c4dee5e1a6148.jpg
Pode rodar esse programa e comprovar que tudo o que escrevemos nao funciona corretamente por causa do codepage:

#include <stdio.h>
#include <locale.h>

int main(void){
  setlocale(LC_ALL, "portuguese-brazilian");
  printf("Utilizando caracteres e acentuação da língua portuguesa!\n");

  printf("\'ç\', ~(ãñõ), `(àèìòù), ´(áéíóúý), ^(âêîôû), ¨(ü)\n");
  printf("\'Ç\', ~(ÃÑÕ), `(ÀÈÌÒÙ), ´(ÁÉÍÓÚÝ), ^(ÂÊÎÔÛ), ¨(Ü)\n\n");

  char frase[100];

  printf("Frase?:");
  scanf("%[^\n]%*c", frase);
  printf("Frase: %s\n\n", frase);

  getchar();
  return 0;
}


Se quiser realmente trabalhar com acentos com a tabela asc2 devemos entender que os 127 primeiros números nunca mudam, mas os outros valores, de 127 até 255 podem mudar conforme a codificação usada, por isso muitas vezes mostram letras estranhas e não o que realmente queremos.

Por default O prompt usa o codepage 850(código de página 850), você pode averiguar digitando no prompt "chcp".
Screenshot_1.jpg.3ed5925342e80094a5df1cc083af3a6a.jpg
É óbvio que esse valor pode ser outro, mas se queremos trabalhar com a entrada correta o código de pagina deveria ser "windows-1252".

Para trabalhar corretamente com acentos devemos seguir alguns passos.
Primeiro setear uma fonte truetype como fonte por default no prompt. Para isso faça o seguinte:
-Abra uma janela do prompt... Aperte a tecla windows+letra R Escreva CMD e aperte aceitar.
Screenshot_3.jpg.32363300e4a355cd1ffca882e2b48a50.jpg

 

Quando abrir a janela do prompt de um click com o botão direito do mouse na área marcada em vermelho, como podemos ver na imagem seguinte.
Screenshot_4.jpg.6834077d841a14bf7a7f321745d2a6f7.jpg

Escolha propriedades
Screenshot_5.jpg.38ea275b7af15eaf08bfb57a8cf9c81f.jpg

 

Ao abrir a janelinha de propriedades, na aba de fontes você vai ver todas as fontes disponíveis para o prompt instaladas, você deve escolher qualquer uma das que tenha Screenshot_7.jpg.de5e41b9db267a87335daad3d2b0b92d.jpg adiante, o TT é de true type.


Recomendo usar a fonte Consolas, é amplamente usada por programadores.
Screenshot_6.jpg.0dbb308aa2c72c79747fd49705c43508.jpg


Com isso já podemos mudar o codepage, para se entender melhor usaremos esse programa:

#include <stdio.h>
#include <windows.h>

int main () {
    unsigned int cp    = 1252;

    //Respaldo do codepage original da consola
    unsigned int cpIn  = GetConsoleCP();        //Se usa para saber o codepage de entrada atual
    unsigned int cpOut = GetConsoleOutputCP(); //Se usa para saber o codepage de saída atual

    //Mostrando o codepage original do prompt
    printf("O codepage de entrada original é: %u\n", cpIn );
    printf("O codepage de saída original é: %u\n\n", cpOut );

    //Estabelecendo o codepage de saída para 1252(latin2).
    SetConsoleOutputCP(cp);
    printf("Já podemos mostrar letras corretamente:\n");
    printf("\'ç\', ~(ãñõ), `(àèìòù), ´(áéíóúý), ^(âêîôû), ¨(ü)\n");
    printf("\'Ç\', ~(ÃÑÕ), `(ÀÈÌÒÙ), ´(ÁÉÍÓÚÝ), ^(ÂÊÎÔÛ), ¨(Ü)\n\n");

    //Estabelecendo o codepage de Entrada para 1252(latin2).
    SetConsoleCP(cp);
    printf("Já podemos ler desde o teclado corretamente:\n\n");
    char frase[100];
    printf("Entre uma frase que contenha acentos da lingua portuguesa?:\n");
    scanf("%[^\n]%*c", frase);
    printf("você escreveu: %s\n\n", frase);

    //É importante voltar o cmd ao estado original, ou pode falhar outros programas.
    //Sempre será possível mudar o codepage usando SetConsoleCP e SetConsoleOutputCP.
    SetConsoleCP(cpIn);
    SetConsoleOutputCP(cpOut);
    printf("Retornando o codepage de entrada ao estado original: %u\n", cpIn );
    printf("Retornando o codepage de saída ao estado  original: %u\n\n", cpOut );

    return 0;
}

 

Como você viu agora funciona corretamente o cmd do windows.

Então seu programa arrumado ficaria:

#include <stdio.h>
#include <windows.h>

int main () {
    char capital[9];

    unsigned int cp    = 1252; //Codepage latin2

    //Respaldo do codepage original da consola
    unsigned int cpin  = GetConsoleCP();
    unsigned int cpout = GetConsoleOutputCP();

    //Mostrando o codepage original do prompt
    printf("O codepage de entrada original é: %u\n", cpin );
    printf("O codepage de saída original é: %u\n\n", cpout );


    SetConsoleCP(cp);       //Estabelecendo o codepage de entrada 1252(latin2)
    SetConsoleOutputCP(cp);  //Estabelecendo o codepage de saída 1252(latin2)

    printf("Digite qual é capital do Brasil: ");
    fgets(capital,9,stdin); // limitar a string a 8 caractes + 1


    if (strcmp(capital,"Brasília")==0) { // COMPARA DUAS STRINGS
        printf("Parabéns\n\n");
    }else {
        printf("**INCORRETO**\n\n O digitado foi %s e o correto é Brasília\n\n",capital);
    }


    printf("Retornando o codepage de entrada ao estado original: %u\n", cpin );
    SetConsoleCP(cpin);
    printf("Retornando o codepage de saída ao estado  original: %u\n\n", cpout );
    SetConsoleOutputCP(cpout);

    return (0);
}

Comente aqui se a solução lhe serviu.

Vei!!! você não sabe o que me custou aprender a corrigir esse problema.... :exorcize: Acredito que algum moderador deveria fixa-lo para outros usuários ter acesso.

Na maioria de lugares que você pesquisar vão lhe recomendar usar o setlocale, e em um principio está muito bem, com ele você pode mostrar os dados de forma satisfatória, mas como você pode averiguar, a entrada não funciona com setlocale, só a saída, por isso anote tudo, pois como falei.... levei anos descobrir, certamente outros podem descobrir em minutos ou horas, mas eu levei anos para dar com a resposta. :tw_bawling:

  • Curtir 4

Compartilhar este post


Link para o post
Compartilhar em outros sites

@vangodp A solução que propus é válida para sistemas baseados em Unix, como o Linux. Esses sistemas usam a codificação UTF-8, que é mais fácil de trabalhar uma vez que entenda como ela funciona, pois os caracteres que pertencem a ASCII original são mantidos intactos e os que estão de fora são precedidos de bytes extras.

Vou tentar explicar com mais detalhes...
Uma descrição bem detalhada da tabela ASCII pode ser encontrada na documentação do seu sistema operacional baseado em Unix através do comando `man ascii`. Resumindo toda essa informação, o ASCII codifica os caracteres em sequências de 7 bits. Como a maioria dos sistemas mapeia nativamente a memória em pedaços de 8 bits ou consegue mapeá-la assim, quando pede-se para visualizar um caractere ASCII comum você verá o oitavo bit, se o sistema é little-endian, ou o primeiro bit se ele é big-endian, como um zero.


A codificação UTF-8 se aproveita dessa característica do ASCII. Dado que os caracteres ASCII nativos tem somente 7 bits preenchidos, ela coloca os caracteres extras em vários bytes que têm um oitavo bit preenchido. Essa maneira simples de codificar os caracteres garantem que seja possível escrever programas em C com suporte básico a muitos caracteres sem usar a `locale.h`.


Como resultado disso, se você usa um `printf("ç\n")`, caso seu editor de texto e seu terminal estejam configurados para UTF-8, tudo ocorrerá perfeitamente. No caso de comparação de strings é necessário saber quantos bytes o caractere extra ocupa, para o caso de usar uma função mais segura como o `fgets()` e definir uma string com o tamanho definido pelo compilador, caso não saiba o tamanho dos caracteres: `char s[] = "çççç"` na hora da comparação.

 

Um bom vídeo de um bom canal (em inglês):
https://www.youtube.com/watch?v=MijmeoH9LT4

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Apenas use string em vez da string da linguagem C, se você está programando em C++, deve evitar ao máximo o uso de de recursos e funções da linguagem C.

 
#include <iostream>
#include <string>
#include <locale.h>

int main()
{
    setlocale(LC_ALL, "Portuguese");

    std::string capital;

    std::cout << "Digite qual é a capital do Brasil: ";
    std::cin >> capital;

    if (capital == "Brasília")
        std::cout << "PARABÉNS" << std::endl;
    else
        std::cout << "**INCORRETO**\n\n O digitado foi " << capital << " e o correto é Brasília\n";
}

 

Editado por cyer
  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

tag do poster esta lá errado. Ou para o autor é tudo igual CC++

  • Haha 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá. Apesar da resposta para o problema já ter sido postada acima de maneira bem eficaz, por sinal, vou postar um forma de resolver a questão d entrada de dados, até aí vai tudo bem. A resposta, no caso Brasília, é lida e impressa da maneira correta no programa.

Mas o negócio complica na hora de fazer a comparação. Daí para tratar essa parte fiz uma "gambiarra legal"

Baseado nisso fiquei pensando se não tem alguma outra codificação q resolva as duas partes: Leitura do valor digitado e o uso adequado da função strcmp() apenas usando a função setlocale()

Apenas para curiosidade, segue o código:

 

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>

int main ()
{
    //----------------------------------------------------
    char capital[15];
    char ch;
    char buffer[20];

    setlocale(LC_ALL, "pt_BR.UTF-8");

    printf("Digite qual e a capital do Brasil: ");
    fgets(capital, 15, stdin);
    strtok(capital, "\n");
    if (strlen(capital) == 15 - 1)
    {
        while ((ch = getchar()) != '\n' && ch != EOF);
    }

    printf("\n%s\n", capital);

    printf("\nTamanho: %d\n", strlen(capital));
    //----------------------------------------------------

    sprintf(buffer, "Bras%clia", 161);

    if (!strcmp(capital, buffer))
    {
        printf("\nParab%cns! Resposta correta\n", 130);
    }
    else
    {
        printf("\nErrou!\n");
    }
}

Não vale rir da resposta q dei para o exercício!

 

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Em um comentário acima foi falado da codificação UTF-8 no caso do Unix e inclusive foi mencionado a questão d, ao usar essa codificação, a maneira d fazer a comparação das strings muda. Só q p mim não ficou claro a forma como fazer essa comparação d maneira adequada. Se alguém tiver alguma sugestão agradeço

adicionado 3 minutos depois

Até onde cheguei foi na parte de resolver a questão de leitura e impressão do texto lido de maneira adequada. 

Mas foi só até essa parte. Inda, ao meu ver, ficou pendente a questão da apresentação do resultado em uma abordagem mais adequada q essa q fiz

adicionado 31 minutos depois

Outra forma d definir a localização e resolver a questão de leitura e impressão de uma String com acentuação brasileira seria essa:

 

setlocale(LC_ALL, "Portuguese_Brasil.1252");

Tal como o autor da resposta mais adequada para esse exercício usou para resolver o mesmo. Mas na hora de fazer a comparação da erro, usando essa codificação na função setlocale() do modo como fiz agora

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eh usaria wide char 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora





Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×