আমি কীভাবে একটি ইনকামিং স্ট্রিং বিভক্ত করব?


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 ডেটা seperated এবং মাইক্রো এসডি কার্ড মধ্যে সঞ্চয় করে
মজিদ mahmoudi

উত্তর:


72

অন্যান্য উত্তরগুলির বিপরীতে, আমি বরং Stringনিম্নলিখিত কারণে দূরে থাকব :

  • গতিশীল মেমরির ব্যবহার (এটি দ্রুত হিপ বিভাজন এবং মেমরির ক্লান্তির দিকে নিয়ে যেতে পারে )
  • নির্মাণ / ধ্বংস / অ্যাসাইনমেন্ট অপারেটরগুলির কারণে বেশ ধীর

আরডুইনোর মতো এম্বেড থাকা পরিবেশে (এমনকি আরও একটি এসএমএএম রয়েছে এমন একটি মেগার জন্যও), আমি বরং স্ট্যান্ডার্ড সি ফাংশন ব্যবহার করব :

  • strchr(): সি স্ট্রিংয়ের একটি অক্ষরের সন্ধান করুন (যেমন char *)
  • strtok(): বিভাজক চরিত্রের ভিত্তিতে একটি সি স্ট্রিং সাবস্ট্রিংগুলিতে বিভক্ত করে
  • atoi(): একটি সি স্ট্রিংকে একটিতে রূপান্তর করে 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
একটি সুন্দর নিখুঁত উত্তর thats! অনেক ধন্যবাদ !
কর্নেলিয়াস

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

এটি একটি খুব ভাল সমাধান মত মনে হচ্ছে আপনাকে ধন্যবাদ। উদাহরণটি এটিকে পুরোপুরি পরিষ্কার করে দেয়
ভাল্রিকরোবট

আমাদের যদি একটি নির্ধারিত সংখ্যক সার্ভো ইনপুট থাকে? আমার উদাহরণে 3 ছিল। তবে যদি কখনও কখনও এটি আরও বেশি বা কম হত। আপনি যেমন একটি দৃশ্য পরিচালনা করার জন্য কোনও পরামর্শ দিতে পারেন
ভাল্রিকরোবট

1
নিশ্চিত: দুটি সম্ভাবনা রয়েছে। 1. সার্ভো সংখ্যাটি প্রথমে প্রেরণ করুন: 3: ভাল 1 এবং ভাল 2 এবং ভাল 3 এবং, লুপটি শুরু করার আগে এই জাতীয় সংখ্যাটি পড়ুন। ২. আপনার আর কোনও সার্ভো নেই বলে বোঝাতে একটি আলাদা টার্মিনেটর ব্যবহার করুন, এটি না পাওয়া পর্যন্ত লুপ করুন: উদাহরণস্বরূপ, ভাল1 এবং ভাল 2 এবং ভাল 3 & #
ড্রড্রি

এই সমাধানটি আপনাকে ভ্যালিকরোবোটকে সাহায্য করেছে, আপনি কি উত্তরটি কার্যকর করতে পারলে দয়া করে তা কার্যকর করতে পারেন?
drodri

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

চিয়ার্স!


এটি সিরিয়াল.আরেড () এর জন্য কাজ করছে না ... কোনও ধারণা কেন? আমি নিম্নলিখিত ত্রুটিটি পেয়েছি:invalid conversion from 'int' to 'char*' [-fpermissive]
আলভারো

4

উদাহরণ দেখুন: https://github.com/BenTommyE/Ardino_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

jfpoil ব্যাখ্যা আরডুইনোতে সিরিয়াল কমান্ড পার্স করার জন্য দুর্দান্ত উত্তর সরবরাহ করেছে। তবে অ্যাটিনি 85 এর দ্বি নির্দেশমূলক সিরিয়াল নেই - সফটওয়্যারসিরিয়াল ব্যবহার করতে হবে। আপনি 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.

সুতরাং বাকী কোডের জন্য প্রচুর স্থান এবং মেমরি রয়েছে


এটিটিনি 85 তে সিরিয়াল থেকে কীভাবে পড়বেন তা আসলে প্রশ্নের অংশ নয়।
গ্রে_গোর

প্রশ্ন থেকে দূরে যাওয়ার জন্য দুঃখিত, তবে অ্যাটিনিউয়ের জন্য সম্প্রদায় এবং সংস্থানগুলি উপলব্ধ আরডুইনোর চেয়ে অনেক ছোট। আমার মতো লোকেরা Arduinoকীওয়ার্ড ব্যবহার করে এবং কখনও কখনও খুব জটিল পরিস্থিতিতে ডুবে থাকে কারণ অ্যাটিনিয়ায় আরডুইনো কোড প্রয়োগ করা সর্বদা তুচ্ছ নয়। আসল কোডকে অ্যাটিনিতে কাজ করতে রূপান্তর করতে হয়েছিল, এটি পরীক্ষা করে কাজ করার সিদ্ধান্ত নিয়েছে
গুডভিল

এই সাইটটি প্রশ্নোত্তর বিন্যাসে আছে। উত্তরগুলির প্রশ্নের উত্তর দেওয়া উচিত। ইতিমধ্যে এটি সম্পর্কিত নয় এমন কিছু যুক্ত করেছেন adds
গ্রে_গোর

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

"স্ট্রিংয়ের মধ্যে একটি স্ট্রিং কীভাবে বিভক্ত করবেন?" প্রশ্নের উত্তর হিসাবে একটি স্ট্রিংকে বিভক্ত করার জন্য এখানে আরডুইনো পদ্ধতি রয়েছে ? বর্তমান প্রশ্নের সদৃশ হিসাবে ঘোষিত।

সমাধানের উদ্দেশ্য হ'ল এসডি কার্ড ফাইলে লগ ইন করা কয়েকটি জিপিএস পজিশনের পার্স করা । স্ট্রিং থেকে প্রাপ্তির পরিবর্তে স্ট্রিংটি ফাইল থেকে পড়ে।Serial

ফাংশনটি StringSplit()একটি স্ট্রিং sLine = "1.12345,4.56789,hello"থেকে 3 স্ট্রিংকে পার্স করা হয় 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("");
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.