15- Cadenas de caracteres en Arduino.

Cadenas y matrices de caracteres Arduino.

Ya nos hemos empapado con los s铆mbolos en la lecci贸n sobre tipos de datos. Perm铆tame recordarle: un s铆mbolo es una variable (o constante) del tipo char y almacena el c贸digo de la letra en la tabla de s铆mbolos. Creado para la conveniencia del programador, para que pueda trabajar no con c贸digos, sino con caracteres legibles. Como en la vida, los s铆mbolos se combinan en palabras, aqu铆 se llaman cadenas (string). Tenemos dos conjuntos de herramientas para trabajar con ellos: cadenas regulares (matrices de caracteres) y cadenas de caracteres.

  • Una matriz de caracteres es simplemente una matriz de datos de tipo char, ya hemos hablado de matrices recientemente y deber铆a comprender de qu茅 estamos hablando. Caracter铆sticas clave: el tama帽o m谩ximo de una matriz de cadenas debe conocerse de antemano y se puede hacer referencia a cada elemento de dicha cadena mediante corchetes. Cualquier texto incluido expl铆citamente en 芦doble comillas禄, es percibido por el programa como una serie de caracteres.
    • Informaci贸n para quienes lean la lecci贸n sobre punteros: al ser una matriz ordinaria, una cadena es un puntero a su primer elemento (es decir, el programa solo sabe exactamente d贸nde comienza la cadena). Las funciones integradas para trabajar con cadenas est谩n guiadas por el car谩cter nulo, que siempre est谩 al final de la cadena. As铆 es como se determina la longitud de la cadena: desde el principio hasta el car谩cter nulo.
  • La principal diferencia entre una cadena de caracteres y una matriz de caracteres es que una cadena es una matriz din谩mica, que no necesita especificarse en tama帽o; puede cambiar durante la operaci贸n del programa. Adem谩s, una cadena no es solo un tipo de datos, sino un objeto de una clase muy poderosa de la biblioteca del mismo nombre. String, que se conecta autom谩ticamente al c贸digo y agrega una gran cantidad de herramientas 煤tiles para trabajar con texto: dividir, recortar, buscar y reemplazar, etc. Una cadena se puede crear a partir de cualquier tipo de datos y volver a convertirlos en casi todos.

Cadenas de strings.

Echemos un vistazo a un gran ejemplo que dejar谩 claro c贸mo declarar una cadena y c贸mo trabajar con ella, adem谩s de tener en cuenta algunas sutilezas:

  String string0 = "Hello cadena" ;             //  palabras entre comillas
  String cadena1 = String ( "lol" ) + Cadena ( "kek" ) ; // suma de dos l铆neas
  String string2 = String ( 'a' ) ;                // cadena de caracteres entre comillas simples
  String string3 = String ( "Esto es una cadena" ) ;   // convertir cadena a cadena
  String string4 = String ( string3 + "m谩s" ) ;  // agrega a string3 texto entre comillas
  String string5 = String ( 13 ) ;                 // convertir de n煤mero a String
  String string6 = String ( 20, DEC ) ;            // convertir de un n煤mero especificando una base (decimal)
  String string7 = String ( 45, HEX ) ;            // convertir de un n煤mero que indica la base (hexadecimal)
  String string8 = String ( 255, BIN ) ;           // convertir de un n煤mero que especifica la base (binario)
  String string9 = String ( 5.698 , 3 ) ;           // de flotante indicando el n煤mero de decimales (aqu铆 3)
  // las cadenas se pueden agregar entre s铆
  string string10 = string0 + string1;         // string10 es igual a Hello String lol kek
 // puede formar un nombre a partir de piezas, por ejemplo, para trabajar con archivos. 
#define NAME "speed"
#define TYPE "-log"
#define EXT ".txt"
  // al agregar, es suficiente especificar String 1 vez para la primera l铆nea
 String filename = String(NAME) + TYPE + EXT; // el nombre del archivo ser谩 igual a speed-log.txt
  // el acceso a un elemento de una cadena funciona con el mismo mecanismo que una matriz
  string0 [ 0 ] = 'a' ; // comillas simples, porque 隆Asigna un S脥MBOLO 脷NICO!
  // ahora en lugar de Hello cadena tenemos aello cadena

Como habr谩 notado, las cadenas se pueden declarar de muchas maneras y, literalmente, puede agregar cadenas como n煤meros con el operador +… Ya dije que las cadenas son objetos de clase String, y esta clase tiene una gran cantidad de m茅todos para trabajar con cadenas, a continuaci贸n los veremos todos con algunos ejemplos. Pero primero, recuerde esto: las cadenas son una herramienta muy pesada, muy lenta y que ocupan mucha memoria: la mera presencia de cadenas (de una o m谩s) en el firmware toma + 5% de la memoria Flash. Para proyectos peque帽os, esto no da miedo, la memoria siempre estar谩 a granel. Adem谩s, el uso incorrecto de cadenas puede provocar la fragmentaci贸n de la RAM y la congelaci贸n del programa, lea m谩s a continuaci贸n.

Herramientas para cadenas de caracteres, String.

Adem谩s del conjunto de m茅todos, la librer铆a String tiene varios operadores sobrecargados, gracias a los cuales podemos:

  • Tratar los elementos String como matrices: myString [ 2 ] = ‘a’ ;
  • Compare las cadenas de String entre s铆: if ( myString1 == myString2 )
  • Comparar cadenas de String con matrices de caracteres: if ( myString1 == 芦kek禄 )
  • Inicialice cadenas de String con cualquier tipo de datos num茅rico, car谩cter, matriz de caracteres y matriz de caracteres dentro de la macro F (): String myString = 10.0 ;
  • Agregue cualquier tipo de datos num茅rico, car谩cter o matriz de caracteres a una cadena: myString + = 12345;

En todos los casos anteriores, podemos asumir que los datos 芦se convertir谩n ellos mismos en una Cadena禄 e interactuar谩n con la Cadena a la izquierda del operador. Esto no es del todo cierto, pero es suficiente para comprenderlo.

Entonces, hay m茅todos para trabajar con cadenas. Como todos los m茅todos, se aplican a sus objetos (a cadenas) mediante un punto. En los ejemplos siguientes, la cadena se denomina myString.

Ver algunos m茅todos en la documentaci贸n de Arduino sobre la clase String:

Longitud de la l铆nea

Un peque帽o comentario sobre la longitud de la cadena: a diferencia de la matriz charsolo puede averiguar la longitud de una cadena String utilizando el m茅todo longitud () (porque una cadena es una matriz din谩mica y sizeof () ejecutado en tiempo de compilaci贸n):

String textString = "Hello";
sizeof(textString);   // devolver谩 6 
textString.length();  // devolver谩 5

Medidas de precauci贸n

Una cadena es esencialmente una matriz din谩mica; su tama帽o puede cambiar durante el programa. Esto es muy conveniente, pero tambi茅n muy peligroso, porque la memoria puede fragmentarse, agotarse, etc. Veamos algunos principios b谩sicos del trabajo seguro con cadenas:

  • Si necesita pasar un String-string a una funci贸n, h谩galo por referencia. Esto evitar谩 que el programa duplique un dato, porque debido a una l铆nea lo suficientemente grande, la RAM puede agotarse y el programa se congelar谩. 
  • Ejemplo: void someFunc (String &str); – la funci贸n toma una referencia a una cadena. Esto no afectar谩 el uso de la funci贸n, pero cuando se llame, 隆no se crear谩 una copia de la cadena!
  • No llame a la conversi贸n a String innecesariamente, 隆la biblioteca lo har谩 por usted! Por ejemplo, no necesitas escribir myString + = String ( valor );, Mas simple myString + = valor;… Al crear una cadena larga agregando nuevos datos 芦pieza por pieza禄, esto le evitar谩 la fragmentaci贸n de la memoria.
  • Envuelva trozos de c贸digo de trabajo duro entre { llaves }: las cadenas de cadenas creadas localmente se eliminar谩n de la memoria inmediatamente despu茅s del cierre }, que puede evitar la fragmentaci贸n y el desbordamiento de la memoria.

Matrices de caracteres.

Las matrices de caracteres, tambi茅n conocidas como 鈥渃har array鈥, son otra forma de trabajar con datos de texto. Esta opci贸n tiene muchas menos posibilidades de trabajar con texto, pero ocupa menos espacio en la memoria (la biblioteca no se usa String) y funciona mucho m谩s r谩pido. Se aplican las mismas reglas a las matrices de caracteres que a las matrices regulares. Considere un ejemplo en el que declaramos matrices de caracteres de diferentes maneras:

// declara una matriz y establece el texto con s铆mbolos
// el compilador calcular谩 el tama帽o
char helloArray [] = { 'H' , 'e' , 'l' , 'l' , 'o' } ;
// pero las matrices de cadenas se pueden declarar as铆:
char helloArray2 [] = "隆Hola!" ;
// puede declarar una matriz m谩s grande que el texto inicial.
// Habr谩 espacio libre para otro texto en el futuro
char helloArray3 [ 100 ] = "隆Hola!" ;
// as铆 es como puedes declarar una cadena larga
char longArray [] = "El zorro marr贸n veloz"
                   "salta sobre el perro perezoso" ;

Puede trabajar con elementos de cadena como con matrices:

helloArray2 [ 0 ] = 'L' ;        // reemplazar el elemento
// ahora helloArray2 == "隆Lello!"

A diferencia de las cadenas, las matrices de caracteres no pueden:

helloArray3 + = textArray; // agregar
textArray = "nuevo texto" ;   // asigna una STRING despu茅s de la inicializaci贸n
if ( helloArray == helloArray2 ) ; // comparar 

Hay funciones especiales para esto, de las que hablaremos a continuaci贸n.

Un punto importante: cualquier 芦texto entre comillas禄 en el c贸digo del programa es una matriz de caracteres, o m谩s bien const char *: un puntero, ya que es una matriz, y una constante, porque el texto se ingres贸 antes de la compilaci贸n y no puede cambiar mientras el programa se est谩 ejecutando. Una matriz de caracteres se puede convertir en una cadena de caracteres (como al principio de la lecci贸n), pero por s铆 sola no tiene nada que ver con la cadena.

Cuando una matriz de caracteres se inicializa con 芦texto entre comillas禄, se crea una matriz con un tama帽o de 1 m谩s que el n煤mero de caracteres del texto: el compilador agrega un car谩cter nulo al final de la cadena NUL, gracias a lo cual varias herramientas de cadena ver谩n la longitud de la cadena: desde el primer car谩cter hasta NUL.

Longitud de la cadena de 芦matriz de caracteres禄

Para determinar la longitud del texto, puede utilizar el operador strlen () que devuelve el n煤mero de caracteres de la matriz. Comparemos su trabajo con el operador sizeof ():

char textArray [ 100 ] = "Mundo" ;
sizeof ( textArray ) ; // devolver谩 100
strlen ( textArray ) ; // devolver谩 5

Aqu铆 operador sizeof () devolvi贸 el n煤mero de bytes ocupados por la matriz. Declar茅 espec铆ficamente la matriz con un tama帽o mayor que el texto que contiene. Y aqu铆 esta el operador strlen () cont贸 y devolvi贸 el n煤mero de caracteres que van desde el principio de la matriz hasta el car谩cter cero (NUL) al final del texto (excluy茅ndolo). Y aqu铆 est谩 el resultado al inicializar sin especificar el tama帽o de la matriz:

char text [] = "Hola" ;
strlen ( text ) ;  // devolver谩 5 (caracteres "legibles")
sizeof ( text ) ;  // devolver谩 6 (bytes)

Matriz de cadenas

Una caracter铆stica muy poderosa de las matrices de caracteres es la capacidad de crear una matriz con varias cadenas y referirse a ellas por n煤mero. Se parece a esto:

// declara una matriz de cadenas
const char * names []   = { 
  "Periodo" ,   // 0
  "Trabajo" ,     // 1
  "Stop" ,     // 2
} ;
// mostrar el tercer elemento
Serial.println(names[2]); // imprimir谩 Stop

Este m茅todo de trabajar con cadenas es bueno porque las cadenas se almacenan bajo n煤meros, y esto es extremadamente conveniente cuando se trabaja con pantallas y, en particular, cuando se crea un men煤 de texto: casi todas las bibliotecas de pantallas pueden mostrar una matriz de caracteres con un comando.

La macro F()

Las cadenas (matrices de caracteres) son una carga muy pesada, porque el texto de una cadena se almacena en la RAM del microcontrolador y no hay tanta. Existe una herramienta lista para usar que le permite almacenar convenientemente datos de texto en la memoria Flash del microcontrolador. Este m茅todo es bueno para enviar datos de texto fijo, por ejemplo, a un monitor de puerto serie o pantalla:

Serial.println(F("Hello, World!"));

La cadena 芦隆Hola, mundo!禄 se escribir谩 en la memoria Flash y no ocupar谩 14 bytes (13 + cero) en la RAM.

Cadenas en la memoria

Las 芦cadenas禄 en una matriz de cadenas tambi茅n se almacenan en la RAM, y la anterior macro F () no aplica para ellos. Es decir, este c贸digo dar谩 lugar a un error:

const char * names []   = { 
  F ( "Periodo" ) ,   // 0
  F ( "Trabajo" ) ,     // 1
  F ( "Stop" ) ,     // 2
} ;

驴C贸mo ser? Podemos almacenar una serie de cadenas en PROGMEM, memoria de programa del microcontrolador, Flash. Esta construcci贸n se puede utilizar como plantilla:

// declaramos nuestras "cadenas"
const char array_1[] PROGMEM = "Period";
const char array_2[] PROGMEM = "Work";
const char array_3[] PROGMEM = "Stop";
// declara la tabla de enlaces
const char* const names[] PROGMEM = {
  array_1, array_2, array_3,
};
void setup() {
  Serial.begin(9600);
  char arrayBuf[10];  //crea buffer
   // copiar a arrayBuf usando el strcpy_P incorporado
  strcpy_P(arrayBuf, (char*)pgm_read_word(&(names[1])));
  Serial.println(arrayBuf); // muestra la salida
}

S铆, es dif铆cil y engorroso, pero con una gran cantidad de datos de texto, 隆esto puede salvar el proyecto! Por ejemplo, al crear un dispositivo con un men煤 de texto en la pantalla. Para obtener m谩s informaci贸n sobre c贸mo almacenar cadenas en PROGMEM, lea el tutorial de PROGMEM.

Herramientas de matrices de caracteres

Hay funciones listas para usar que le permiten convertir varios tipos de datos en cadenas:

  • itoa ( int_data, str, base ) – escribe una variable de tipo En t int_data para encadenar str con base * base .
  • ltoa (long_data, str, base )  – escribe una variable de tipo largo long_data para encadenar str con base * base .
  • ultoa ( unsigned_long_data, str, base )  – escribe una variable de tipo  unsigned_long_data a str con * base .
  • dtostrf(float_data, width, dec, str) – escribe una variable de tipo float_data en cadena str con el n煤mero de caracteres de width y decimales dec .

* Nota: la base es la base del sistema de c谩lculo, como cuando se env铆a a SerialPort:

  • DIC – decimal
  • BIN – binario
  • OCT – octal
  • HEX – hexadecimal
float x = 12,123 ;
char str [ 10 ] = "" ;
dtostrf ( x, 4, 2, str ) ;
// aqu铆 str == "12.12"
int y = 123;
itoa ( y, str, DEC ) ;
// aqu铆 str == "123"

Por el contrario, puede convertir cadenas en datos num茅ricos (la funci贸n devolver谩 el resultado):

  • atoi ( str )– convertir str en int
  • atol ( str )– convertir str en long
  • atof ( str )– convertir str en float
float x;
char str[10] = "12.345";
x = atof(str);
// aqui x == "12.345"

隆Atenci贸n! Las funciones de conversi贸n que funcionan con el tipo flotante son muy pesadas: 隆su 芦conexi贸n禄 ocupa ~ 2 KB de memoria Flash! Evite usarlos en un proyecto grande tanto como sea posible. Para convertir, puede crear su propia funci贸n, se pueden encontrar opciones casi listas para todos los tipos de datos en el Arduino Print.cpp est谩ndar ( enlace al archivo en Arduino github).

Las matrices de caracteres no son tan simples como parecen: la biblioteca cstring est谩ndar mejora enormemente sus capacidades. El uso de todos los trucos disponibles para trabajar con matrices de caracteres le permite eliminar por completo las cadenas de caracteres pesadas de su c贸digo y hacerlo m谩s ligero, r谩pido y 贸ptimo. Repasaremos brevemente las herramientas m谩s importantes de manipulaci贸n de cadenas en cstring .

Un punto importante: la biblioteca funciona con cadenas como con punteros, y muchas funciones devuelven un puntero como resultado. 驴C贸mo entender esto si no ha le铆do la lecci贸n sobre punteros y / o el tema es demasiado complejo? El puntero es el primer car谩cter de la l铆nea, el trabajo con la l铆nea comenzar谩 con 茅l. El 煤ltimo car谩cter es nulo NUL car谩cter, y para el programa, la cadena existe exactamente en este rango. Si alguna funci贸n devuelve un puntero a un car谩cter espec铆fico en una cadena, de hecho, devuelve una parte de la cadena desde este car谩cter hasta el final de la cadena. Por ejemplo, busc谩bamos el s铆mbolo 芦 , 禄 en la l铆nea 芦隆Hola Mundo!禄. El programa nos devolver谩 un puntero a esta coma, de hecho ser谩 una parte de la misma cadena que contiene禄, mundo!禄… El 芦comienzo禄 de la l铆nea simplemente cambiar谩.

Herramientas de cstring

Puede ver una colecci贸n de m茅todos de cstring en este tutorial.


Deja un comentario