Hoe kan ik een inkomende string splitsen?

Ik stuur een lijst met servoposities via de seriële verbinding naar de arduino in het volgende formaat

1:90&2:80&3:180 

Wat zou worden geparseerd als:

servoId : Position & servoId : Position & servoId : Position

Hoe zou ik deze waarden opsplitsen en converteren naar een geheel getal?

Reacties

  • ik heb slave (arduino uno) string verzenden via serieel 30; 12.4; 1 en 1 master (esp8266) recive string ik wil in master gescheiden gegevens hebben zoals 30 12.4 1 en deze opslaan op micro sd-kaart

Answer

In tegenstelling op andere antwoorden, “blijf ik liever weg van String om de volgende redenen:

  • dynamisch geheugengebruik (dat kan snel leiden tot heapfragmentatie en geheugenuitputting )
  • vrij traag vanwege constr uction / destructie / toewijzingsoperatoren

In een embedded omgeving zoals Arduino (zelfs voor een Mega die meer SRAM heeft), zou ik “liever standaard C-functies :

  • strchr() : zoek naar een teken in een C-string (bijv char *)
  • strtok() : splitst een C-string in subtekenreeksen, gebaseerd op een scheidingsteken
  • atoi() : converteert een C string naar een int

Dat zou leiden tot het volgende codevoorbeeld:

 // 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, "&"); }  

Het voordeel hiervan is dat er geen dynamische geheugentoewijzing plaatsvindt; je kunt zelfs input declareren als een lokale variabele binnen een functie die de commandos zou lezen en uitvoeren; zodra de functie is geretourneerd, wordt de grootte ingenomen door input (in de stapel) hersteld.

Reacties

  • Hadn ‘ t dacht aan het geheugenprobleem. dit is geweldig.
  • Uitstekend. Mijn antwoord was zeer ” arduino ” gebaseerd en met behulp van typische Arduino SDK-functies waaraan een nieuwe gebruiker gewend zou kunnen zijn, maar dit antwoord is wat moet er worden gedaan voor ” productie ” systemen. Probeer in het algemeen te ontsnappen aan dynamische geheugentoewijzing in embedded systemen.

Answer

Deze functie kan worden gebruikt om een string in stukken te scheiden op basis van het scheidingsteken.

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

String converteren naar int

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

Dit stuk code neemt een string en scheidt het op basis van een bepaald teken en geeft het item tussen het scheidingsteken terug

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]) : ""; } 

Opmerkingen

  • dat is een prachtig perfect antwoord! heel erg bedankt!
  • Je zou deze moeten kiezen. (Duim omhoog)
  • elegant! werkt perfect om sleutelwaardeparen te verzenden

Answer

Je zou zoiets als het volgende kunnen doen, maar houd er rekening mee account verschillende dingen:

Als u readStringUntil() gebruikt, zal het wachten tot het het teken of de time-outs ontvangt. Dus met je huidige string, zal de laatste positie iets langer duren, omdat het moet wachten. U kunt een afsluitende & toevoegen om deze time-out te vermijden. U kunt dit gedrag eenvoudig op uw monitor controleren, probeer de string met en zonder de extra & te verzenden en u zult een dergelijke time-outvertraging zien.

Dat doet u werkelijk heb je de servo-index niet nodig, je kunt gewoon je string met posities verzenden en de servo-index ophalen door de waarde positie in de string, zoiets als: 90&80&180&. Als je de servo-index gebruikt, wil je deze misschien controleren (converteer naar int, en pas dan de lusindex i aan) om er zeker van te zijn dat er niets mis ging met je bericht.

Je moet controleren of de terugkerende string van readStringUntil niet leeg is. Als de functie time-outs heeft, heb je “niet genoeg gegevens ontvangen, en dus zal elke poging om je int waarden te extraheren vreemde resultaten opleveren.

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); } } } 

Opmerkingen

  • Dit lijkt een zeer goede oplossing, bedankt. Het voorbeeld maakt het perfect duidelijk
  • Wat als we hadden een ongedefinieerd aantal servo-ingangen? in mijn voorbeeld waren er 3. Maar wat als het soms meer of minder was. Kunt u een suggestie geven voor het omgaan met een dergelijk scenario
  • Zeker: er zijn twee mogelijkheden. 1 .Stuur eerst het aantal servos: 3: val1 & val2 & val3 &, lees zon nummer voordat de lus wordt gestart. 2. Gebruik een andere terminator om aan te geven dat je geen servos meer hebt, loop door tot je hem vindt: val1 & val2 & val3 & #, bijvoorbeeld.
  • Blij dat deze oplossing je heeft geholpen, @ValrikRobot, kun je het antwoord valideren als het nuttig was?
  • of jij kan gewoon de for verwijderen, en dus werkt de code gewoon elke keer dat je een commando verstuurt.

Answer

U kunt Stream.readStringUntil(terminator) gebruiken om voor elk onderdeel een andere terminator door te geven.

Op elk onderdeel je roept dan String.toInt

Answer

De eenvoudigste oplossing is om sscanf () te gebruiken.

 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); 

Dit geeft de volgende output:

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

Proost!

Reacties

  • Het werkt niet voor serial.read () … elke i waarom? Ik krijg de volgende foutmelding: invalid conversion from 'int' to 'char*' [-fpermissive]

Antwoord

Zie voorbeeld op: 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; } 

Antwoord

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; } } } 

Answer

jfpoilpret leverde geweldig antwoord voor het parseren van seriële commandos op Arduino. Attiny85 heeft echter geen bidirectionele serieel – SoftwareSerial moet worden gebruikt. Zo port u dezelfde code voor 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, "&"); } } }  

Attiny85-schemas voor pincodes voer de afbeeldingsbeschrijving hier in

Sketch compileert in:

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. 

Er is dus voldoende ruimte en geheugen voor de rest van de code

Opmerkingen

  • Hoe te lezen uit serienummer op een ATtiny85 is niet ‘ t echt een deel van de vraag.
  • Sorry voor wijken af van de vraag, maar de community en bronnen die beschikbaar zijn voor Attiny zijn veel kleiner dan voor Arduino. Mensen zoals ik die naar antwoorden zoeken, gebruiken het trefwoord Arduino en komen soms in zeer lastige situaties terecht bij het implementeren Attiny is niet altijd triviaal. Moest originele code converteren om op Attiny te werken, testte het werkend en besloot om te sha re it
  • Deze site is in Q & A-formaat. Antwoorden zouden de vraag moeten beantwoorden. De jouwe voegt gewoon iets toe dat ‘ er niets mee te maken heeft.

Antwoord

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 } 

Antwoord

Hier is Arduino methode om een string te splitsen als antwoord op de vraag “Hoe een string in een substring te splitsen?” gedeclareerd als een duplicaat van de huidige vraag.

Het doel van de oplossing is om een reeks GPS posities ingelogd op een SD-kaart bestand. In plaats van een string te ontvangen van Serial, wordt de string gelezen uit een bestand.

De functie is StringSplit() parse een string sLine = "1.12345,4.56789,hello" tot 3 strings sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: de invoerregels die moeten worden geparseerd,
  2. char cDelim: het scheidingsteken tussen parameters,
  3. String sParams[]: de uitvoerarray van parameters,
  4. int iMaxParams: het maximum aantal parameters,
  5. Uitvoer int: het aantal geparseerde parameters,

De functie is gebaseerd op String::indexOf() en 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); } 

En het gebruik is heel eenvoudig:

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(""); } 

Antwoord

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(){} 

Antwoord

Het is geen antwoord op uw vraag, maar het kan voor iemand nuttig zijn. Als uw string een specifiek voorvoegsel heeft, kunt u eenvoudig startsWith en substring gebruiken. Bijv.

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

En stuur dan height 10 wat 10 .

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *