Como faço para dividir uma string de entrada?

Estou enviando uma lista de posições de servo por meio da conexão serial para o arduino no seguinte formato

1:90&2:80&3:180 

Que seria analisado como:

servoId : Position & servoId : Position & servoId : Position

Como dividir esses valores e convertê-los em um inteiro?

Comentários

  • eu tenho escravo (arduino uno) enviar string via serial 30; 12.4; 1 e 1 master (esp8266) string recive eu quero no master ter dados separados como 30 12.4 1 e salvá-los no cartão micro SD

Resposta

Contrariamente para outras respostas, prefiro ficar longe de String pelos seguintes motivos:

  • uso de memória dinâmica (que pode levar rapidamente a fragmentação de heap e esgotamento de memória )
  • bastante lento devido a constr Operadores de ação / destruição / atribuição

Em um ambiente embarcado como o Arduino (mesmo para um Mega que tenha mais SRAM), prefiro usar funções C padrão :

  • strchr() : pesquisa por um caractere em uma string C (ou seja char *)
  • strtok() : divisões uma string C em substrings, com base em um caractere separador
  • atoi() : converte um C string para uma int

Isso levaria ao seguinte exemplo de código:

 // Calculate based on max input size expected for one command #define INPUT_SIZE 30 ... // Get next command from Serial (add 1 for final 0) char input[INPUT_SIZE + 1]; byte size = Serial.readBytes(input, INPUT_SIZE); // Add the final 0 to end the C string input[size] = 0; // Read each command pair char* command = strtok(input, "&"); while (command != 0) { // Split the command in two values char* separator = strchr(command, ":"); if (separator != 0) { // Actually split the string in 2: replace ":" with 0 *separator = 0; int servoId = atoi(command); ++separator; int position = atoi(separator); // Do something with servoId and position } // Find the next command in input string command = strtok(0, "&"); }  

A vantagem aqui é que nenhuma alocação dinâmica de memória ocorre; você pode até declarar input como uma variável local dentro de uma função que leria os comandos e os executaria; quando a função é retornada, o tamanho ocupado por input (na pilha) é recuperado.

Comentários

  • Hadn ‘ t pensou no problema de memória. isso é ótimo.
  • Excelente. Minha resposta foi muito ” arduino ” baseada e usando funções do SDK Arduino típicas com as quais um novo usuário poderia estar mais acostumado, mas esta resposta é o que deve ser feito para sistemas ” produção “. Em geral, tente escapar da alocação de memória dinâmica em sistemas embarcados.

Resposta

Esta função pode ser usada para separar uma string em pedaços com base no caractere de separação.

String xval = getValue(myString, ":", 0); String yval = getValue(myString, ":", 1); Serial.println("Y:" + yval); Serial.print("X:" + xval); 

Converter String em int

int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(yval); 

Este pedaço de código recebe uma string e separa com base em um determinado caractere e retorna O item entre o caractere de separação

String getValue(String data, char separator, int index) { int found = 0; int strIndex[] = { 0, -1 }; int maxIndex = data.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } 

Comentários

  • essa é uma resposta linda e perfeita! muito obrigado!
  • Você deve escolher este. (Polegar para cima)
  • elegante! funciona perfeitamente para enviar pares de valores-chave

Resposta

Você poderia fazer algo como o seguinte, mas leve em consideração conta várias coisas:

Se você usar readStringUntil(), ele irá esperar até receber o caractere ou tempo limite. Assim, com sua string atual, a última posição vai durar um pouco mais, pois tem que esperar. Você pode adicionar um & final para evitar esse tempo limite. Você pode facilmente verificar esse comportamento em seu monitor, tente enviar a string com e sem o & extra e você verá um atraso de tempo limite.

Você realmente faz não precisa do índice do servo, você pode apenas enviar sua string de posições e obter o índice do servo pela posição do valor na string, algo como: 90&80&180&. Se você usar o índice servo, talvez queira verificá-lo (converter para int e, em seguida, combinar o índice de loop i) para garantir que nada deu errado com sua mensagem.

Você deve verificar se a string de retorno de readStringUntil não está vazia. Se a função expirar, você não recebeu dados suficientes e, portanto, qualquer tentativa de extrair seus int valores produzirá resultados estranhos.

void setup() { Serial.begin(9600); } void loop() { for(int i=1; i<=3; i++) { String servo = Serial.readStringUntil(":"); if(servo != ""){ //here you could check the servo number String pos = Serial.readStringUntil("&"); int int_pos=pos.toInt(); Serial.println("Pos"); Serial.println(int_pos); } } } 

Comentários

  • Esta parece uma solução muito boa, obrigado. O exemplo esclarece perfeitamente
  • E se tivéssemos um número indefinido de entradas do servo? no meu exemplo havia 3. Mas e se às vezes fosse mais ou menos. Você pode oferecer alguma sugestão para lidar com tal cenário
  • Claro: há duas possibilidades. 1 .Envie primeiro o número de servos: 3: val1 & val2 & val3 &, leia tal número antes de iniciar o loop. 2. Use um terminador diferente para indicar que você não tem mais servos, faça um loop até encontrá-lo: val1 & val2 & val3 & #, por exemplo.
  • Que bom que esta solução ajudou você, @ValrikRobot, você poderia validar a resposta se ela foi útil?
  • ou você pode simplesmente remover o para, e assim o código funcionará sempre que você enviar um comando.

Resposta

Você pode usar Stream.readStringUntil(terminator) passando um terminador diferente para cada parte.

Em cada parte você então liga para String.toInt

Resposta

A solução mais simples é usar sscanf () .

 int id1, id2, id3; int pos1, pos2, pos3; char* buf = "1:90&2:80&3:180"; int n = sscanf(buf, "%d:%d&%d:%d&%d:%d", &id1, &pos1, &id2, &pos2, &id3, &pos3); Serial.print(F("n=")); Serial.println(n); Serial.print(F("id1=")); Serial.print(id1); Serial.print(F(", pos1=")); Serial.println(pos1); Serial.print(F("id2=")); Serial.print(id2); Serial.print(F(", pos2=")); Serial.println(pos2); Serial.print(F("id3=")); Serial.print(id3); Serial.print(F(", pos3=")); Serial.println(pos3); 

Isso dá a seguinte saída:

n=6 id1=1, pos1=90 id2=2, pos2=80 id3=3, pos3=180 

Saudações!

Comentários

  • Não está funcionando para serial.read () … any i dea porque? Recebo o seguinte erro: invalid conversion from 'int' to 'char*' [-fpermissive]

Resposta

Veja o exemplo em: https://github.com/BenTommyE/Arduino_getStringPartByNr

// splitting a string and return the part nr index split by separator String getStringPartByNr(String data, char separator, int index) { int stringData = 0; //variable to count data part nr String dataPart = ""; //variable to hole the return text for(int i = 0; i<data.length()-1; i++) { //Walk through the text one letter at a time if(data[i]==separator) { //Count the number of times separator character appears in the text stringData++; } else if(stringData==index) { //get the text when separator is the rignt one dataPart.concat(data[i]); } else if(stringData>index) { //return text and stop if the next separator appears - to save CPU-time return dataPart; break; } } //return text if this is the last part return dataPart; } 

Resposta

String getValue(String data, char separator, int index) { int maxIndex = data.length() - 1; int j = 0; String chunkVal = ""; for (int i = 0; i <= maxIndex && j <= index; i++) { chunkVal.concat(data[i]); if (data[i] == separator) { j++; if (j > index) { chunkVal.trim(); return chunkVal; } chunkVal = ""; } else if ((i == maxIndex) && (j < index)) { chunkVal = ""; return chunkVal; } } } 

Resposta

jfpoilpret forneceu uma ótima resposta para analisar o comando serial no Arduino. No entanto Attiny85 não tem serial bidirecional – SoftwareSerial deve ser usado. É assim que você transfere o mesmo código para Attiny85

 #include <SoftwareSerial.h> // Calculate based on max input size expected for one command #define INPUT_SIZE 30 // Initialize SoftwareSerial SoftwareSerial mySerial(3, 4); // RX=PB3, TX=PB4 // Parameter for receiving Serial command (add 1 for final 0) char input[INPUT_SIZE + 1]; void setup() { mySerial.begin(9600); } void loop() { // We need this counter to simulate Serial.readBytes which SoftwareSerial lacks int key = 0; // Start receiving command from Serial while (mySerial.available()) { delay(3); // Delay to allow buffer to fill, code gets unstable on Attiny85 without this for some reason // Don"t read more characters than defined if (key < INPUT_SIZE && mySerial.available()) { input[key] = mySerial.read(); key += 1; } } if (key > 0) { // Add the final 0 to end the C string input[key] = 0; // Read each command pair char* command = strtok(input, "&"); while (command != 0) { // Split the command in two values char* separator = strchr(command, ":"); if (separator != 0) { // Actually split the string in 2: replace ":" with 0 *separator = 0; int servoId = atoi(command); ++separator; int position = atoi(separator); } // Find the next command in input string command = strtok(0, "&"); } } }  

esquemas Attiny85 para números de pinos insira a descrição da imagem aqui

O Sketch é compilado em:

Sketch uses 2244 bytes (27%) of program storage space. Maximum is 8192 bytes. Global variables use 161 bytes (31%) of dynamic memory, leaving 351 bytes for local variables. Maximum is 512 bytes. 

Portanto, há bastante espaço e memória para o resto do código

Comentários

  • Como ler em série em um ATtiny85 não ‘ realmente faz parte da pergunta.
  • Desculpe por divergindo da pergunta, mas a comunidade e os recursos disponíveis para Attiny são bem menores do que para Arduino. Pessoas como eu em busca de respostas usam a palavra-chave Arduino e às vezes se metem em situações complicadas ao implementar o código do Arduino Attiny nem sempre é trivial. Tive que converter o código original para funcionar no Attiny, testei e decidi re it
  • Este site está no formato Q & A. As respostas devem responder à pergunta. O seu apenas adiciona algo que ‘ não tem relação com ele.

Resposta

char str[] = "1:90&2:80&3:180"; // test sample serial input from servo int servoId; int position; char* p = str; while (sscanf(p, "%d:%d", &servoId, &position) == 2) { // process servoId, position here // while (*p && *p++ != "&"); // to next id/pos pair } 

Resposta

Aqui está Arduino método para dividir uma string como resposta à pergunta “Como dividir uma string em uma substring?” declarada como uma duplicata da questão presente.

O objetivo da solução é analisar uma série de GPS posições registradas em um arquivo cartão SD . Em vez de receber uma string de Serial, a string é lida do arquivo.

A função é StringSplit() parse uma String sLine = "1.12345,4.56789,hello" a 3 Strings sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: as linhas de entrada a serem analisadas,
  2. char cDelim: o caractere delimitador entre os parâmetros,
  3. String sParams[]: a matriz de saída de parâmetros,
  4. int iMaxParams: o número máximo de parâmetros,
  5. Saída int: o número de parâmetros analisados parâmetros,

A função é baseada em String::indexOf() e String::substring():

int StringSplit(String sInput, char cDelim, String sParams[], int iMaxParams) { int iParamCount = 0; int iPosDelim, iPosStart = 0; do { // Searching the delimiter using indexOf() iPosDelim = sInput.indexOf(cDelim,iPosStart); if (iPosDelim > (iPosStart+1)) { // Adding a new parameter using substring() sParams[iParamCount] = sInput.substring(iPosStart,iPosDelim-1); iParamCount++; // Checking the number of parameters if (iParamCount >= iMaxParams) { return (iParamCount); } iPosStart = iPosDelim + 1; } } while (iPosDelim >= 0); if (iParamCount < iMaxParams) { // Adding the last parameter as the end of the line sParams[iParamCount] = sInput.substring(iPosStart); iParamCount++; } return (iParamCount); } 

E o uso é muito simples:

String sParams[3]; int iCount, i; String sLine; // reading the line from file sLine = readLine(); // parse only if exists if (sLine.length() > 0) { // parse the line iCount = StringSplit(sLine,",",sParams,3); // print the extracted paramters for(i=0;i<iCount;i++) { Serial.print(sParams[i]); } Serial.println(""); } 

Resposta

void setup() { Serial.begin(9600); char str[] ="1:90&2:80"; char * pch; pch = strtok(str,"&"); printf ("%s\n",pch); pch = strtok(NULL,"&"); //pch=next value printf ("%s\n",pch); } void loop(){} 

Resposta

Não é uma resposta à sua pergunta, mas pode ser útil para alguém. Se sua string tiver um prefixo específico, você pode usar simplesmente startsWith e substring. Por exemplo,

void loop () if(Serial.available()){ command = Serial.readStringUntil("\n"); Serial.println("Received command: " + command); if(command.startsWith("height")) { Serial.println(command.substring(7)); } } } 

E, em seguida, envie height 10 o que irá extrair 10 .

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *