Ir ao conteúdo
  • Cadastre-se

C++ Problema para ler uma string com acento


Posts recomendados

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
Link para o comentário
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
Link para o comentário
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
  • Obrigado 1
  • Amei 2
Link para o comentário
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 3
Link para o comentário
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";
}

 

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

  • 3 semanas depois...

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!

 

Link para o comentário
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

Link para o comentário
Compartilhar em outros sites

  • 1 ano depois...
  • 4 meses depois...
  • 1 ano depois...
Em 14/04/2018 às 23:30, vangodp disse:

asc2

 

O II de ASCII significa Information Interchange. Já tinha lido antes asc2, mas lembro que não existe relação entre o 2 e II como seria 2 em romanos, assim como nunca existiu asc1 ou asc3 for that matter. Apenas ASCII, American Standard Code for Information Interchange, contemporâneo ao EBCDIC --- de Extended Binary Coded Decimal Interchange Code --- da IBM, que era  a outra codificação da época.

 

Em 10/02/2020 às 22:23, Marcos Ranes disse:

system("chcp 1252 > nul");

 

Evite system() a todo custo. Está usando Windows e C então tem acesso a tudo no sistema. E aí chamar o shell do sistema e passar uma string com um comando é um pouco ingênuo. Não estará fazendo nada exceto passar uma string.

 

Em Windows o comando em C é 
 

https://docs.microsoft.com/en-us/windows/console/setconsoleoutputcp
 

	SetConsoleOutputCP( 1252 );

 

E o certo é ler qual a página em uso e salvar, mudar para a que quer usar e restaurar a original na saída. Não é certo mudar a configuração do sistema a partir de um programa e DEIXAR alterado. . .

 

Para ler a página use

 

	unsigned pagina_orginal = GetConsoleOutputCP();
	SetConsoleOutputCP( 1252 );

	// ... e na saida

	SetConsoleOutputCP( pagina_original );

	// ...

 

É o civilizado. :) 

 

Em 14/04/2018 às 23:30, vangodp disse:

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

 

Tem razão, @vangodp: isso é um 1nf3rn0 pra dizer o mínimo. Mas não tem de fato solução e o problema mesmo aparece na hora de ler e gravar em arquivos e comparar essas coisas. E classificar. 

 

A página de código recomendada para o Windows é 65001 há anos. O Windows usa Unicode. Codepages estão documentadas aqui https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers 

 

Hoje tem os emoji e milhares de símbolos diferentes, e os caracteres podem usar de 1 a 4 bytes cada um, e é um inferno usar isso. Fuja sempre que possível e use por exemplo um navegador. Unicode tem capacidade para mais de um milhão de símbolos. E ao gerar um arquivo com símbolos possivelmente multi-byte é preciso saber a ordem em que os bytes são gravados, e assim precisa gravar (e ler) no início do arquivo algo chamado BOM que indica qual byte bem primeiro no disco para cada par. E depois disso tratar os caracteres como tal. E imagine classificar um arquivo em ordem alfabética. Como fazer com as letras sem saber se foi gravado em um sistema que usava hebraico? ou algo tipo cirilico? Como comparar Á e à com A? Em que codificação?

 

image.png.3f68728fc7f9dcd79f616ca62e38465e.pngSai caro isso. 

 

O código do dinheiro aqui à esquerda? 01F4B8, 3 bytes :D 

 

 

O problema é que é preciso ter um parâmetro ao ler algo no sistema, dizendo qual o encoding usado para gerar o arquivo, e HTML tem isso prontinho. 

 

 

 

 

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário 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 comunidades 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

×
×
  • Criar novo...