将字符串拆分为字符串数组

14
我一直在玩arduino编程,但今天我遇到了一个问题,我不能用我非常有限的C知识解决。 事情是这样的。 我正在创建一个PC应用程序,将串行输入发送到arduino(deviceID、command、commandparameters)。该arduino将通过RF将该命令传输到其他arduino。根据设备ID,正确的arduino将执行命令。 为了能够确定设备ID,我想在“,”上拆分该字符串。 这就是我的问题,我知道如何在Java中轻松完成此操作(即使不使用标准的split函数),但是在C中完全不同。 你们中的任何人都可以告诉我如何让它工作吗? 谢谢
/*
  Serial Event example

 When new serial data arrives, this sketch adds it to a String.
 When a newline is received, the loop prints the string and 
 clears it.

 A good test for this is to try it with a GPS receiver 
 that sends out NMEA 0183 sentences. 

 Created 9 May 2011
 by Tom Igoe

 This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/SerialEvent

 */

String inputString;         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
String[] receivedData;

void setup() {
    // initialize serial:
    Serial.begin(9600);
    // reserve 200 bytes for the inputString:
    inputString.reserve(200);
}

void loop() {
    // print the string when a newline arrives:
    if (stringComplete) {
        Serial.println(inputString); 
        // clear the string:
        inputString = "";
        stringComplete = false;
    }
}

/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
    while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read(); 
        if (inChar == '\n') {
            stringComplete = true;
        } 
        // add it to the inputString:
        if(stringComplete == false) {
            inputString += inChar;
        }
        // if the incoming character is a newline, set a flag
        // so the main loop can do something about it:
    }
}

String[] splitCommand(String text, char splitChar) {
    int splitCount = countSplitCharacters(text, splitChar);
    String returnValue[splitCount];
    int index = -1;
    int index2;

    for(int i = 0; i < splitCount - 1; i++) {
        index = text.indexOf(splitChar, index + 1);
        index2 = text.indexOf(splitChar, index + 1);

        if(index2 < 0) index2 = text.length() - 1;
        returnValue[i] = text.substring(index, index2);
    }

    return returnValue;
}

int countSplitCharacters(String text, char splitChar) {
    int returnValue = 0;
    int index = -1;

    while (index > -1) {
        index = text.indexOf(splitChar, index + 1);

        if(index > -1) returnValue+=1;
    }

    return returnValue;
} 

我已经决定使用strtok函数了。现在我遇到了另一个问题,错误如下:

SerialEvent.cpp: 在函数 'void splitCommand(String, char)' 中:

SerialEvent:68: 错误: 无法将'String'转换为'char*',作为参数传递给'char* strtok(char*, const char*)'

SerialEvent:68: 错误: 'null' 在此范围内未声明

代码如下:

String inputString;         // a string to hold incoming data

void splitCommand(String text, char splitChar) {
    String temp;
    int index = -1;
    int index2;

    for(temp = strtok(text, splitChar); temp; temp = strtok(null, splitChar)) {
        Serial.println(temp);
    }

    for(int i = 0; i < 3; i++) {
        Serial.println(command[i]);
    }
}

1
看看 strtok() 函数。 - Kerrek SB
2
strtok已过时,请使用strsep - waspinator
为了以后的参考,据我所知,strtok()并没有被弃用。MS Visual C++编译器将其标记为不安全,并提供了替代方案,GNU/POSIX也是如此(虽然提供的替代方案不同)。只要正确使用并意识到其缺点,它就能正常运行。 - Toby
strtok 不是解决这个问题的好方法:它会将任何连续的,视为单个分隔符。此外,它可能在 Arduino 平台上不可用。 - chqrlie
6个回答

45

这是一个老问题,但我创建了一些代码片段可能有所帮助:

 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]) : "";
}
该函数返回一个由预定义字符分隔的单个字符串,并指定索引位置。例如:
String split = "hi this is a split test";
String word3 = getValue(split, ' ', 2);
Serial.println(word3);

应该输出 'is'。你也可以尝试使用索引 0 返回 'hi' 或尝试安全地使用索引 5 返回 'test'。

希望这能帮到你!


1
这太棒了。谢谢。只是要提醒,它仅按单个字符拆分。 我使用带有“〜”的字符串来拆分项目和“~~”用于新行,但它不起作用。 - Roberto Trevisan
请告诉我应该往哪里送啤酒!非常棒的解决方案,完美运作。 - LucasRT

10

实施:

int sa[4], r=0, t=0;
String oneLine = "123;456;789;999;";

for (int i=0; i < oneLine.length(); i++)
{ 
 if(oneLine.charAt(i) == ';') 
  { 
    sa[t] = oneLine.substring(r, i).toInt(); 
    r=(i+1); 
    t++; 
  }
}

结果:

    // sa[0] = 123  
    // sa[1] = 456  
    // sa[2] = 789  
    // sa[3] = 999

1
欢迎来到 Stack Overflow。请通过提供一些背景和解释来避免仅代码答案。请参见 http://stackoverflow.com/help/how-to-answer。 - Uwe Allner
1
不确定问题出在哪里,我看到了一个有效的实现和一个非常简单的示例,演示如何基于分隔符拆分字符串并存储值。 唯一的问题是他忘记添加“.toInt()”(考虑我们想将值存储到int数组中)。一切看起来都很好。 干得好@Jan - MSA

0

对于动态分配内存,您需要使用malloc函数,例如:

String returnvalue[splitcount];
for(int i=0; i< splitcount; i++)
{
    String returnvalue[i] = malloc(maxsizeofstring * sizeof(char));
}

您还需要最大字符串长度。


1
你不一定需要使用 malloc()。如果在分割操作和数据传输之间字符串不会发生变化,那么保留指向原始字符串中各个位置的指针是完全安全的。这样做还更快、占用更少的内存,并且减少了潜在的内存泄漏。 - japreiss
是的,那可能行得通,你只需要手动跟踪每个字符串的长度/结尾以避免重叠,因为除了最后一个字符串外,其他字符串都没有 '\0' 终止字符。 - 3Pi

0

我认为你的想法是一个很好的起点。这里有一段代码,我用它来解析带有以太网盾的HTTP GET REST请求。

思路是使用while循环和lastIndexOf,并将字符串存储到数组中(但你也可以做其他事情)。

"request"是你想要解析的字符串(对我来说它被称为request,因为...它就是这样)。

    int goOn = 1;
    int count = -1;
    int pos1;
    int pos2 = request.length();

    while( goOn == 1 ) {
        pos1 = request.lastIndexOf("/", pos2);
        pos2 = request.lastIndexOf("/", pos1 - 1);

        if( pos2 <= 0 ) goOn = 0;

        String tmp = request.substring(pos2 + 1, pos1);

        count++;
        params[count] = tmp;

        // Serial.println( params[count] );

        if( goOn != 1) break;
    }
    // At the end you can know how many items the array will have: count + 1 !

我已经成功使用了这段代码,但是当我尝试打印params[x]时,我认为存在编码问题...我也是一个初学者,所以我不太懂字符和字符串的区别...

希望能有所帮助。


0

基于分隔符拆分字符串的 C 方法是使用 strtok (或 strtok_r)。

请参见 this 问题。

strtok 已经被弃用,请使用 strsep 代替。 - waspinator
被谁弃用了?十分钟的谷歌搜索只找到了在Microsoft Visual Studio C++中被弃用的内容。OP正在编程Arduino,它使用自己的C版本,因此Windows中的函数是否被弃用完全无关紧要。 - markgz

0

我相信这是最直接和最快的方法:

String strings[10]; // Max amount of strings anticipated

void setup() {
  Serial.begin(9600);
  
  int count = split("L,-1,0,1023,0", ',');
  for (int j = 0; j < count; ++j)
  {
    if (strings[j].length() > 0)
      Serial.println(strings[j]);
  }
}

void loop() {
  delay(1000);
}

// string: string to parse
// c: delimiter
// returns number of items parsed
int split(String string, char c)
{
  String data = "";
  int bufferIndex = 0;

  for (int i = 0; i < string.length(); ++i)
  {
    char c = string[i];
    
    if (c != ',')
    {
      data += c;
    }
    else
    {
      data += '\0';
      strings[bufferIndex++] = data;
      data = "";
    }
  }

  return bufferIndex;
}

你确定这个有效吗?在split函数中,c是一个参数和一个局部变量。此外,逗号不是动态使用的。 - Whitebird

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接