들어오는 문자열을 어떻게 분할합니까?

다음 형식으로 직렬 연결을 통해 arduino에 서보 위치 목록을 보냅니다.

1:90&2:80&3:180 

다음과 같이 구문 분석됩니다.

servoId : Position & servoId : Position & servoId : Position

이 값을 어떻게 분할하고 다음으로 변환합니까? 정수?

코멘트

  • 나는 슬레이브 (arduino uno)가 시리얼 30; 12.4; 1 및 1 마스터 (esp8266) 수신 문자열을 통해 문자열을 보냅니다. 마스터에서 30 12.4 1과 같은 데이터를 분리하고 마이크로 SD 카드에 저장하고 싶습니다.

답변

반대로 다른 답변에 대해서는 다음과 같은 이유로 String에서 벗어나고 싶습니다.

  • 동적 메모리 사용 (즉, 힙 조각화 메모리 고갈 )
  • constr로 인해 상당히 느림 경매 / 파괴 / 할당 연산자

Arduino와 같은 임베디드 환경 (SRAM이 더 많은 Mega에서도)에서는 표준 C 함수 :

  • strchr() : C 문자열에서 문자 검색 (예 : char *)
  • strtok() : 분할 구분자 문자를 기반으로 C 문자열을 하위 문자열로 변환
  • atoi() : C를 변환합니다. 문자열을 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 (스택에서)가 차지하는 크기가 복구됩니다.

댓글

  • 메모리 문제를 ‘ 생각하지 않았습니다. 훌륭합니다.
  • 훌륭합니다. 내 대답은 매우 ” arduino ” 기반이며 새로운 사용자가 더 익숙 할 수있는 일반적인 arduino SDK 기능을 사용했지만이 대답은 ” 생산 ” 시스템에 대해 수행해야하는 작업 일반적으로 임베디드 시스템의 동적 메모리 할당에서 벗어나십시오.

Answer

이 기능을 사용할 수 있습니다. 구분 문자가 무엇인지에 따라 문자열을 조각으로 분리합니다.

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

문자열을 정수로 변환

int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(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]) : ""; } 

코멘트

  • 그것은 아름다운 완벽한 대답입니다! 고마워요!
  • 이걸 선택하세요. (좋아요)
  • 우아 해요! 키 값 쌍을 보내는 데 완벽하게 작동합니다.

답변

다음과 같은 작업을 수행 할 수 있지만 몇 가지 사항을 고려하십시오.

readStringUntil()를 사용하는 경우 문자 또는 시간 초과를 수신 할 때까지 대기합니다. 따라서 현재 문자열에서 마지막 위치는 기다려야하기 때문에 조금 더 오래 지속됩니다. 이 시간 초과를 방지하려면 후행 &를 추가 할 수 있습니다. 모니터에서이 동작을 쉽게 확인하고 추가 &를 포함하거나 포함하지 않고 문자열을 보내면 이러한 시간 초과 지연이 표시됩니다.

실제로 수행합니다. 서보 인덱스가 필요하지 않은 경우 위치 문자열을 보내고 문자열의 값 위치로 서보 인덱스를 가져올 수 있습니다 (예 : 90&80&180&). 서보 인덱스를 사용하는 경우 메시지에 문제가 없는지 확인하기 위해 (int로 변환 한 다음 루프 인덱스 i와 일치) 확인할 수 있습니다.

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 .먼저 서보 수 : 3 : val1 & val2 & val3 &, 읽기 루프를 시작하기 전에 이러한 번호. 2. 다른 터미네이터를 사용하여 더 이상 서보가 없음을 표시하고 찾을 때까지 반복합니다. val1 & val2 & val3 & #입니다.
  • 이 솔루션이 도움이 되었으니 다행입니다. @ValrikRobot, 답변이 유용했는지 확인해 주시겠습니까?
  • 또는 귀하 for를 제거 할 수 있으므로 명령을 보낼 때마다 코드가 작동합니다.

Answer

Stream.readStringUntil(terminator) 를 사용하여 각 부분에 대해 다른 종결자를 전달할 수 있습니다.

각 부분에서 그런 다음 String.toInt

Answer

가장 간단한 해결책은 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]

Answer

예제 참조 : 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; } 

답변

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

답변

jfpoilpret 는 훌륭한 답변을 제공했습니다. / a> 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는 다음으로 컴파일됩니다.

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. 

따라서 나머지 코드를위한 충분한 공간과 메모리가 있습니다.

댓글 h3>

  • ATtiny85에서 시리얼을 읽는 방법은 ‘ 실제로 질문의 일부가 아닙니다.
  • 죄송합니다. 질문과는 다르지만 Attiny에 사용할 수있는 커뮤니티 및 리소스는 Arduino보다 훨씬 작습니다. 답을 찾는 저와 같은 사람들은 Arduino 키워드를 사용하고 때때로 Arduino 코드를 구현할 때 매우 까다로운 상황에 처하게됩니다. Attiny는 항상 사소한 것은 아닙니다. 원본 코드를 Attiny에서 작동하도록 변환해야하고 작동하는지 테스트 한 다음 sha로 결정했습니다. re it
  • 이 사이트는 Q & A 형식입니다. 대답은 질문에 답해야합니다. 귀하의 이메일은 ‘ 관련이없는 항목을 추가합니다.

답변

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 } 

답변

다음은 Arduino입니다. 메서드는 다음과 같이 선언 된 “문자열을 부분 문자열로 분할하는 방법?” 질문에 대한 답으로 문자열을 분할합니다. 현재 질문의 중복입니다.

솔루션의 목적은 일련의 GPS 를 구문 분석하는 것입니다. SD 카드 파일에 로그인 된 위치. Serial에서 문자열을받는 대신 파일에서 문자열을 읽습니다.

함수는 StringSplit() 구문 분석입니다. a 문자열 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(""); } 

답변

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

답변

귀하의 질문에 대한 답변은 아니지만 누군가에게 유용 할 수 있습니다. 문자열에 특정 접두사가있는 경우 startsWithsubstring를 사용할 수 있습니다. 예 :

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

그런 다음 height 10를 전송하여 10를 추출합니다. .

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다