Programar em C++/Manipulando strings: diferenças entre revisões

[edição não verificada][edição não verificada]
Conteúdo apagado Conteúdo adicionado
Daveiro (discussão | contribs)
Sem resumo de edição
 
Daveiro (discussão | contribs)
Sem resumo de edição
Linha 6:
 
----
Strings - Escrita – char, string
 
Recordar que os caracteres são entendidos como sendo números, esses números são traduzidos na tabela ASCII.
O utilizador pode querer introduzir mais do que um carácter e podem ser organizados num array, sendo este array de caracteres terminado pelo null carácter. Ester tipo de palavra é ao estilo de C-string que é o modelo anterior ao c++.
 
Lendo um caractere
Ler um caractere até é simples, basta utilizar a função cin e será guardado o valor digitado na variável.
char grade;
cout << "Enter a grade: ";
cin >> grade;
Porém teremos de pressionara a tecla ENTER no final de digitar o input. Isto leva a várias questões.
 
 
O problema “press any key to continue”
#include <iostream>
using namespace std;
 
int main(void)
{
char ch;
do {
cout << "Press Q or q to quit, any other key to continue: ";
cin >> ch;
if (ch != 'Q' && ch != 'q')
cout << "You want to continue?\n";
else
cout << "You quit";
} while (ch != 'Q' && ch != 'q');
system (“pause”);
return 0;
}
 
O programa funciona bem se pressionarmos Q ou q para sairmos.
O programa funciona bem se pressionarmos outra qualquer tecla printable
Mas se pressionarmos a tecla ENTER, nada acontece, o cin continua á espera de input. a razão é o operador de extração “>>” ignora os espaços em branco e os newline resultantes de pressionar a tecla enter
 
A função cin.get
Já tivemos oportunidade para discutir a função getline (função membro) do objecto cin.
cin.getline(name,80)
Aqui vamos utilizar uma outra função a cin.get
Esta função pode ser chamada (tal como a getline) através de 3 argumentos, (onde o primeiro é o array de caracteres), mas também o pode ser com zero argumentos ou ainda 1 argumento.
 
No caso de ser zero argumentos apenas irá ler um caractere, em vez de ser um array de caracteres.
 
No caso de ter um argumento, ela aceita qualquer tecla incluindo o enter. (o que não se passa com o cin e o operador de extracção). Aqui um exemplo
#include <iostream>
using namespace std;
 
int main(void)
{
char ch;
do {
cout << "Press Q or q to quit, any other key to continue: ";
cin.get(ch);
if (ch != 'Q' && ch != 'q')
cout << "You want to continue?\n";
else
cout << "You quit";
} while (ch != 'Q' && ch != 'q');
system (“pause”);
return 0;
}
Porém se pressionarmos um printable carácter, não conseguimos inserir o próximo prompt, parece que houve um salto. Estranho!
 
Para explicar a razão deste novo problema necessitamos de explicar o conceito de buffer.
 
O input buffer é uma área de memória que guarda input, por exemplo do keyboard, até que esse imput seja atribuído pelo cin e o operador de extracção >>, ou por funções get ou getline do objecto cin
 
Quando o loop começa o imput buffer está vazio.
• Se digitarmos apenas o enter, sendo este o primeiro e único caractere no imput buffer, ele é removido do imput buffer e atribuído á variavel ch.
• Então o imput buffer está vazio na próxima iteração do loop.
• Se digitarmos x e enter. Temos 2 caracteres. A função get retira o primeiro carácter do imput buffer e atribui á variavel ch. Mas nisto o carácter new line permanece no imput buffer. Isto faz com que na próxima iteração do loop, não haja a oportunidade para entrar com dados.
Ou seja na segunda iteração, é retirado o newline – que ficou da 1ª iteração - e é colocado na varaivel ch. Agora o input buffer está vazio.
 
cin.ignore
Uma solução é limpar o newline caractere do imput buffer antes da chamada da função getline. E fazemos isso usando a função ignore do objecto cin.
 
Esta função membro tal com a get e a getline são overloaded. Podem ser chamadas com zero, um ou dois argumentos.
 
Utilizando a função ignore com zero argumentos, permite que o próximo carácter no imput buffer seja lido e depois descartado- e isto é exactamente aquilo que queríamos.
 
A função com 1 ou 2 argumentos é usada para arrays de caracteres.
• Com um argumento, o argumento é o número máximo de caracters a ser removido do imput buffer.
exemplo cin.ignore(80) – remove até 80caracteres do imput bufer
• Com dois argumentos, o segundo argumento é o caractere se encontrado, antes do número de caracteres epecificado no primeiro paramento, faz com que a remoção do imput buffer pare.
Exemplo: cin.ignore (80, ‘\n’), remove 80 caracteres se até lá não encontrar o newline.
 
 
Reescrevendo o código anterior utilizando o cin.ignore
#include <iostream>
using namespace std;
 
int main(void)
{
char ch;
do {
cout << "Press Q or q to quit, any other key to continue: ";
cin.get(ch);
cin.ignore();
if (ch != 'Q' && ch != 'q')
cout << "You want to continue?\n";
else
cout << "You quit";
} while (ch != 'Q' && ch != 'q');
system (“pause”);
return 0;
}
 
Ora este programa funciona muito bem, MAS…
Se pressionarmos a tecla Enter para continuar, teremos de fazer isso duas vezes, pois a primeira vez parece ter sido ignorada. A razão: é que não existe nada no input buffer quando a função ignore é chamada, por isso é que a função enter necessita de ser pressionada 2 vezes por forma a colocar qq coisa no buffer para a função ignore remover.
 
Bem isto está a tornar-se cansativo.
 
Bem e se tentarmos modificar isto através do if?
#include <iostream>
using namespace std;
 
int main(void)
{
char ch;
do {
cout << "Press Q or q to quit, any other key to continue: ";
cin.get(ch);
if (ch != '\n')
cin.ignore();
if (ch != 'Q' && ch != 'q') // é um erro de principiante utilizar o “||” em vez de &&, pois a varivel ch irá ter apenas um valor de cada vez
cout << "You want to continue?\n";
else
cout << "You quit";
} while (ch != 'Q' && ch != 'q');
system (“pause”);
return 0;
}
 
Agora sim temos todos os problemas resolvidos e isto agora funciona!!
 
 
cin, cin.get, cin.getline
O problema anterior do newline caracter permanece quando usamos o cin, o get e o getline juntos num programa, uma vez que o enter é usado para terminar o input.
#include <iostream>
using namespace std;
 
int main(void)
{
char name[80];
int courseNum;
cout << "Enter course number: ";
cin >> courseNum;
cout << "Enter your name: ";
cin.getline(name, 80);
cout << "Course number is: " << courseNum << endl;
cout << "Your name is: " << name << endl;
system (“pause”);
return 0;
}
Aqui neste exemplo nós não tivemos a oportunidade de colocar o nome. Quando digitamos o número e depois pressionamos a tecla enter o cin coloca o número no courseNUm mas permanenece o newline no inputbuffer que fica para o enter name, poi o getline lê whitespace.
A solução pode ser:
 
#include <iostream>
using namespace std;
 
int main(void)
{
char name[80];
int courseNum;
cout << "Enter course number: ";
cin >> courseNum;
cin.ignore();
cout << "Enter your name: ";
cin.getline(name, 80);
cout << "Course number is: " << courseNum << endl;
cout << "Your name is: " << name << endl;
system (“pause”);
return 0;
}
 
A partir destes exemplos podemos criar umas regras
 
1. Colocar sempre a função ignore depois do cin e do >>
razão: o cin>>deixa sempre o newline no imputbuffer. assim devemos eliminá-lo com a função ignore
 
2. Não colocar a função ignore (no caso de ser sem parâmetros) depois do getline
razão:o getline remove o newline que termina o imput do imput buffer, portanto não é necessário o ignore.
 
3. Verificar se temos o newline no imputbuffer depois de utilizar o get, se tivermos deveremos utilizar o ignore.
razão: a função get com um argumento deixa o newline no imputbuffer se pressionarmos um carácter e o enter. mas não deixará se apenas pressionarmos o enter. portanto é necessário confirmar.
 
Funções de caracteres úteis.
As seguintes funções estão na biblioteca ficheiro <cctype>
 
toupper function – (to+upper) retornar a maiúscula de uma letra. é uma função de um argumento – o carácter. no caso do argumento não ser uma letra, a função retorna o mesmo carácter que é argumento.
 
tolower function – (to+lower) o mesmo caso que toupper
 
 
#include <iostream>
#include <cctype>
using namespace std;
 
int main(void)
{
char ch;
do {
cout << "Press Q or q to quit, any other key to continue: ";
cin.get(ch);
ch = toupper(ch);
if (ch != '\n')
cin.ignore();
if (ch != 'Q')
cout << "You want to continue?\n";
else
cout << "You quit";
} while (ch != 'Q');
system (“pause”);
return 0;
}
 
Funções que verificam o carácter. Estas funções recebem apenas um argumento, o carácter e retornam um boolean value
Table 12-1: Functions that Check the Value of Characters
Function Description
isalpha Returns true if the argument is a letter of the alphabet; false if otherwise.
isalnum Returns true if the argument is a letter of the alphabet or a digit; false if otherwise.
isdigit Returns true if the argument is a digit; false if otherwise.
islower Returns true if the argument is a lower case letter of the alphabet, false otherwise.
isprint Returns true if the argument is a printable character (including a space created by pressing the spacebar); false if otherwise.
ispunct Returns true if the argument is a punctuation character (printable character other than a letter, digit or space); false if otherwise.
isupper Returns true if the argument is an uppercase letter of the alphabet; false if otherwise.
isspace Returns true if the argument is a whitespace character (tab, newline, or space); false if otherwise.
 
 
Funções uteis para o uso de strings
strlen – (str=string + len=length)- aceita um argumento que pode ser um array de caracteres, um ponteiro (que aponta para um array de caracteres) ou uma string literal. retorna um número inteiro que representa o número de caracteres, não incluindo o null caracter
int len;
len = strlen("Jeff") // len is 4
char* stinkydog = "Dante";
len = strlen(stinkydog); // len is 5
char name[80] = "Devvie";
len = strlen(name); // len is 6
 
 
o c++ tem duas funções similares que são o lenght e size. Nenhuma destas funções tem argumentos e ambas retornar um inteiro que representa o tamanho das strings
string s = "Jeff Kent";
cout << s.length(); // outputs 9
cout << s.size(); // also outputs 9
 
 
 
Copiando strings
Se tentássemos copiar strings desta maneira
char* target = "Jeff Kent";
char source[80] = "Micaela";
target = source;
O que acontecia é que era a cópia do address do source para o ponteiro e não o valor.
 
No entanto existe a função strcpy (estilo C) – ela aceita dois argumentos,
• O primeiro é para onde vai ser copiada e é passado o ponteiro desse array (não pode ser uma string literal).
• O segundo é a frase a ser copiada e pode ser um array, um ponteiro ou um string literal
 
char* target = "Jeff Kent";
char source[80] = "Micaela";
strcpy(target, source);
 
Porem em C++ podemos atribuir o valor de uma variável para outra da classe string da forma
string target = "Jeff Kent";
string source = "Micaela";
target = source;
 
 
Unir strings
strcat – (string+concatenate) – une duas frases. Recebe 2 arguentos, a frase primária – o ponteiro para esse array.
char target[80] = "Jeff";
char* source= " Kent";
strcat(target, source);
cout << target; // outputs "Jeff Kent"
 
ao estilo de c++ podemos fazer.
string target = "Jeff";
string source = " Kent";
target += source;
cout << target; // outputs "Jeff Kent"
 
 
comparar frases
se fizessemos
char str1[80] = "Devvie Kent";
char str2[80] = "Devvie Kent";
if (str1 == str2)
cout << "The two C-strings are equal";
else
cout << "The two C-strings are not equal";
 
o que acontecia é que estariamos a comparar os endereços e não os valores
 
temos a função strcmp (string+compare) (tem 2 arguentos. retornar 0 se forem iguais
 
char str1[80] = "Devvie Kent";
char str2[80] = "Devvie Kent";
if (!strcmp(str1, str2))
cout << "The two C-strings are equal";
else
cout << "The two C-strings are not equal";
esta comparação pode ser resultar em negativo e positivo e isso tem a ver com o jogo de caracteres na tabela ascII. aqui vai um resumo
Table 12-2: Results of String Comparisons
First String (str1) Second String (str2) strCmp(str1, str2) Reason
Jeff jeff negative j has a higher ASCII value than J
aZZZ Zaaa positive a has a higher ASCII value than Z
chess check positive First three characters are the same, but fourth letter of first C-string, s, has higher ASCII value than fourth character of second C-string, c.
Jeff Jeffrey negative First four characters are the same, but fifth character of second C-string, r, has a higher ASCII value than null character in the fifth position of the first C-string
 
 
 
 
Convertendo C-string e numero
Na biblioteca ficheiro cstdlib (c+std+lib) temos várias funções de conversão de números em tipologia numérica
 
 
atoi (acrónimo para ASCII to integer) recebe um argumento – c-string) e retorna o inteiro que a c-string representa. Não verifica se o argumento pode ser convertido
int num = atoi("7654");
 
#include <iostream>
#include <cstring>
using namespace std;
 
int main(void)
{
char input[80];
int num;
cout << "Enter an integer: ";
cin >> input;
for (int x = 0; x < strlen(input); x++)
{
if (x == 0)
{
if (!isdigit(input[x]) && input[x] != '-')
return false;
}
else
{
if (!isdigit(input[x]))
return false;
}
}
num = atoi(input);
cout << num;
system (“pause”);
return 0;
}
 
Neste exemplo temos a vantagem de o utilizado inserir um digito para o array de caracteres em vez de um int para evitar um run-time error ou garbage data que aconteceria ase o imput fosse não numérico.
Depois o array é verificado a ver se representa um número. Se o numero for negativo tem o “–“.
 
Ao estilo c++ temos a função atol ou atof. Uma alternativa útil é atribuir o valor da string ao array de caracteres usando a função c_str e depois usar o ataoi, atol ou atof
string name = "123";
char str1[80];
strcpy(str1, name.c_str()); //ou strcpy (str1, name.data());
int num = atoi(str1);
 
A função itoa (3 argumentos: o numero, o pointeiro ao array, e a base numérica de conversão)
char intArray[20];
itoa (776, intArray, 10);
 
 
 
====
Lendo um caractere
Ler um caractere até é simples, basta utilizar a função cin e será guardado o valor digitado na variável.
char grade;
cout << "Enter a grade: ";
cin >> grade;
Porém teremos de pressionara a tecla ENTER no final de digitar o input. Isto leva a várias questões.
 
 
Se eu quisesse guardar o meu nome “José”
char a[4]
cout << “digite o seu nome:”;
cin >> a[1] >> a[1] >> a[2] >> a[3];
teria portanto de criar quantas variáveis forem as letras digitadas.
o cin irá associar a cada letra digitada a posição no array. no fim vou digitar o enter para finalizar o input.
 
 
agora se eu quisesse guardar o meu nome com duas palavras “José Miguel”
o que nós faríamos era colocar mais uma variavel para o espaço em branco entre as palavras. mas o problema é que o compilador interpretaria o espaço em branco como o fim do input, e portanto terminaria a leitura e passava para a linha de execução seguinte.
 
getline
se utilizarmos o getline
#include <iostream>
#include <string> //repare que adicionámos a biblioteca “string”
using namespace std;
int main()
{
string nome; //string é um conjunto de char e como se vê tem a sua tipologia
cout << “digite o seu nome”;
getline (cin,nome); /*é uma função como se vê pelo uso de () e tem dois parâmetros,
um o cin diz para utilizar o que for digitado e colocar no Segundo parametro com de nome““nome”*/
cout << nome; //manda imprimir o valor da variavel string chamada de “nome”
system (“pause”);
return 0;
}
repare que não necessitamos de explicitar a quantidade de letras que vamos utilizar. grande evolução.
temos no entanto de adicionar a biblioteca string.
 
 
sstream
esta biblioteca ficheiro que é a string-stream, permite que as strings sejam interpretadas como um stream. assim poderemos executar operações com as strings (de e para), o que é especialmente útil para converter as strings em valores numéricos
string frase (“1204”);
int numero;
stringstream (frase)>>numero
isto permite pegar no número colocá-lo como um caractere e depois fazer a associação para um int.
 
qual é a vantagem deste sistema: é que agora temos controlo sobre o input.
se não utilizássemos a artimanha de colocar o imput como caracteres e depois converter em numero, ou seja se colocássemos directamente como número, o que aconteceria é estávamos a dar a hipótese do utilizador escrever uma letra. o que é que acontecia?
o compilador iria ignorar a letra e não iria colocar nada na variavel. e depois na leitura iria ler a variavel onde tínhamos era o valor residual, o valor que estava antes na memória. portanto iríamos ter um valor sem nexo nenhum.