मैं आने वाली स्ट्रिंग को कैसे विभाजित कर सकता हूं?


51

मैं निम्नलिखित प्रारूप में धारावाहिक कनेक्शन के माध्यम से सर्वो पदों की एक सूची भेज रहा हूं

1:90&2:80&3:180

जिसे निम्नलिखित के रूप में देखा जाएगा:

servoId : Position & servoId : Position & servoId : Position

मैं इन मूल्यों को कैसे विभाजित करूँगा, और उन्हें एक पूर्णांक में बदल दूंगा?


12.4; मैं दास (arduino uno) धारावाहिक 30 के माध्यम से स्ट्रिंग भेजने 1 और 1 मास्टर (esp8266) Recive स्ट्रिंग मैं चाहता हूँ मास्टर की तरह 30 12.4 1 डेटा अलग और सूक्ष्म एसडी कार्ड में सहेज है
मजीद mahmoudi

जवाबों:


72

अन्य उत्तरों के विपरीत, मैं Stringनिम्नलिखित कारणों से दूर रहना चाहता हूँ :

  • डायनामिक मेमोरी यूसेज (जिससे जल्दी से विखंडन और मेमोरी थकावट हो सकती है )
  • निर्माण / विनाश / असाइनमेंट ऑपरेटरों के कारण काफी धीमा

Arduino (जैसे मेगा के लिए अधिक SRAM है) जैसे एम्बेडेड वातावरण में, मैं मानक C फ़ंक्शन का उपयोग करूंगा :

  • strchr(): सी स्ट्रिंग (यानी char *) में एक चरित्र के लिए खोज
  • strtok(): एक विभाजक चरित्र के आधार पर, सब्सट्रिंग्स में एक सी स्ट्रिंग को विभाजित करता है
  • atoi(): C स्ट्रिंग को a में कनवर्ट करता है int

यह निम्नलिखित कोड नमूने की ओर ले जाएगा:

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

यहां लाभ यह है कि कोई गतिशील मेमोरी आवंटन नहीं होता है; आप inputकिसी फ़ंक्शन के अंदर एक स्थानीय चर के रूप में भी घोषित कर सकते हैं जो कमांड पढ़ेंगे और उन्हें निष्पादित करेंगे; एक बार फ़ंक्शन वापस आने पर input(स्टैक में) द्वारा कब्जा कर लिया गया आकार वापस मिल जाता है।


स्मृति के मुद्दे के बारे में नहीं सोचा था। यह महान है।
वैलरीकॉर्ब

4
अति उत्कृष्ट। मेरा जवाब बहुत ही "आर्कडिनो" आधारित था और ठेठ अर्दीनो एसडीके कार्यों का उपयोग करके जो एक उपन्यास उपयोगकर्ता को अधिक उपयोग किया जा सकता है, लेकिन यह उत्तर "उत्पादन" प्रणालियों के लिए किया जाना चाहिए। सामान्य तौर पर, एम्बेडेड सिस्टम में गतिशील मेमोरी आवंटन से बचने की कोशिश करें।
द्रोड़ी

22

इस फ़ंक्शन का उपयोग अलग-अलग चरित्र के आधार पर एक स्ट्रिंग को टुकड़ों में अलग करने के लिए किया जा सकता है।

String xval = getValue(myString, ':', 0);
String yval = getValue(myString, ':', 1);

Serial.println("Y:" + yval);
Serial.print("X:" + xval);

स्ट्रिंग को इंट में बदलें

int xvalue = stringToNumber(xval);
int yvalue = stringToNumber(yval);

कोड का यह हिस्सा एक स्ट्रिंग लेता है और एक दिए गए चरित्र के आधार पर इसे अलग करता है और अलग करने वाले चरित्र के बीच आइटम वापस करता है

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

1
एक सुंदर सही जवाब! आपका बहुत बहुत धन्यवाद !
Curnelious

11

आप निम्नलिखित कुछ कर सकते हैं, लेकिन कृपया कई बातों पर ध्यान दें:

यदि आप उपयोग करते हैं readStringUntil(), तो यह तब तक इंतजार करेगा जब तक कि यह चरित्र या टाइमआउट प्राप्त न कर ले। इस प्रकार, आपकी वर्तमान स्ट्रिंग के साथ, अंतिम स्थिति थोड़ी लंबी हो जाएगी, क्योंकि उसे इंतजार करना होगा। आप &इस समय से बचने के लिए एक अनुगामी जोड़ सकते हैं । आप अपने मॉनीटर में इस व्यवहार को आसानी से देख सकते हैं, स्ट्रिंग को अतिरिक्त के बिना और बिना भेजने की कोशिश करें &और आपको इस तरह की समय सीमा में देरी होगी।

आप वास्तव में इमदादी सूचकांक की जरूरत नहीं है, तुम सिर्फ पदों के अपने स्ट्रिंग भेज सकते हैं, और स्ट्रिंग में, कुछ की तरह मूल्य स्थिति से इमदादी सूचकांक मिलती है: 90&80&180&। यदि आप सर्वो इंडेक्स का उपयोग करते हैं, तो शायद आप इसे जांचना चाहते हैं (कन्वर्ट करें int, और फिर लूप इंडेक्स से मेल करें) यह सुनिश्चित करने के लिए कि आपके संदेश में कुछ भी गलत नहीं हुआ है।

आपको जांचना होगा कि रिटर्निंग स्ट्रिंग readStringUntilखाली नहीं है। यदि फ़ंक्शन टाइमआउट करता है, तो आपको पर्याप्त डेटा नहीं मिला है, और इस प्रकार आपके intमूल्यों को निकालने का कोई भी प्रयास अजीब परिणाम देगा।

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

यह एक बहुत अच्छा समाधान की तरह लगता है धन्यवाद। उदाहरण इसे पूरी तरह से साफ करता है
ValrikRobot

क्या होगा अगर हमारे पास अपरिभाषित संख्या का सर्वो इनपुट है? मेरे उदाहरण में 3 था। लेकिन क्या होगा अगर कभी-कभी यह अधिक, या कम था। क्या आप इस तरह के परिदृश्य से निपटने के लिए कोई सुझाव दे सकते हैं
ValrikRobot

1
निश्चित: दो संभावनाएँ हैं। 1. सर्व की संख्या पहले भेजें: 3: val1 और val2 & val3 &, लूप शुरू करने से पहले ऐसी संख्या पढ़ें। 2. जब तक आप इसे न पाएं, तब तक यह बताने के लिए एक अलग टर्मिनेटर का उपयोग करें कि आपके पास कोई अधिक इमदादी, लूप न हो: उदाहरण के लिए val1 & val2 & val3 & #।
द्रोड़ी

खुशी है कि इस समाधान से आपको @ValrikRobot में मदद मिली, क्या आप उपयोगी होने पर उत्तर को मान्य कर सकते हैं?
डोडरी

1
या आप केवल इसके लिए हटा सकते हैं, और इसलिए कोड किसी भी समय आपके द्वारा कमांड भेजने पर काम करेगा।
लेस्टो


4

सरलतम समाधान 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);

यह निम्न आउटपुट देता है:

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

चीयर्स!


यह serial.read () के लिए काम नहीं कर रहा है ... किसी भी विचार क्यों? मुझे निम्न त्रुटि मिलती है:invalid conversion from 'int' to 'char*' [-fpermissive]
अल्वारो

4

उदाहरण देखें: 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;
}

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

2

jfpoilpret ने Arduino पर सीरियल कमांड को पार्स करने के लिए शानदार जवाब दिया । हालाँकि Attiny85 में द्विदिश सीरियल नहीं है - SoftwareSerial का उपयोग किया जाना है। यह है कि आप 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 योजनाबद्ध यहाँ छवि विवरण दर्ज करें

स्केच संकलन:

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.

इसलिए बाकी कोड के लिए बहुत जगह और मेमोरी है


कैसे एक ATtiny85 पर धारावाहिक से पढ़ने के लिए वास्तव में सवाल का हिस्सा नहीं है।
gre_gor

प्रश्न से हटने के लिए क्षमा करें, लेकिन समुदाय और संसाधन उपलब्ध अर्टिनो के लिए की तुलना में छोटा है। मेरे जैसे लोग उत्तर का उपयोग करते हुए Arduinoकीवर्ड का उपयोग करते हैं और कभी-कभी बहुत मुश्किल परिस्थितियों में पहुंच जाते हैं क्योंकि अर्टिनी पर Arduino कोड लागू करना हमेशा तुच्छ नहीं होता है। अटारी पर काम करने के लिए मूल कोड को बदलना पड़ा, इसे काम करने का परीक्षण किया और इसे साझा करने का निर्णय लिया
goodevil

यह साइट प्रश्नोत्तर प्रारूप में है। जवाब सवाल का जवाब देना चाहिए। तुम्हारा बस कुछ है कि यह असंबंधित है कहते हैं।
gre_gor

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

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

-1

यहाँ प्रश्न के उत्तर के रूप में स्ट्रिंग को विभाजित करने के लिए Arduino विधि है "सबस्ट्रिंग में स्ट्रिंग को कैसे विभाजित किया जाए?" वर्तमान प्रश्न के डुप्लिकेट के रूप में घोषित किया गया।

समाधान का उद्देश्य एसडी कार्ड फ़ाइल में लॉग की गई जीपीएस स्थितियों की एक श्रृंखला को पार्स करना है । स्ट्रिंग प्राप्त करने के बजाय , स्ट्रिंग को फ़ाइल से पढ़ा जाता है।Serial

समारोह 3 StringSplit()स्ट्रिंग के sLine = "1.12345,4.56789,hello"लिए एक स्ट्रिंग है sParams[0]="1.12345", sParams[1]="4.56789"और sParams[2]="hello"

  1. String sInput: इनपुट लाइनों को पार्स किया जाना है,
  2. char cDelim: मापदंडों के बीच सीमांकक चरित्र,
  3. String sParams[]: मापदंडों का आउटपुट सरणी,
  4. int iMaxParams: मापदंडों की अधिकतम संख्या,
  5. आउटपुट int: पार्स किए गए मापदंडों की संख्या,

समारोह पर आधारित है String::indexOf()और 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);
}

और उपयोग वास्तव में सरल है:

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("");
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.