Go Down

Topic: Convert String string1 to char* (Read 17 times) previous topic - next topic

sholland

Sep 13, 2010, 02:51 am Last Edit: Sep 13, 2010, 02:52 am by sholland Reason: 1
Hi,

Sorry if the subject isn't right.. I'm quite new at programming in C/C++.. I've done a fair bit of PERL but it feels quite different with the variables.

This code works:
Code: [Select]

     char* filename = "file.txt";
     if (! file.open(&root, filename, O_READ)) {
        ........
      }


But I've used String (object) to contain the filename and this code does not.

Code: [Select]

     String filename = "file.txt"
     if (! file.open(&root, filename, O_READ)) {
         ......
     }


It produces this error

Code: [Select]

error: no matching function for call to 'SdFile::open(SdFile*, String&, const uint8_t&)'
/home/shawn/arduino/arduino-0019/libraries/SdFat/SdFat.h:220: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
/home/shawn/arduino/arduino-0019/libraries/SdFat/SdFat.h:221: note:                 uint8_t SdFile::open(SdFile*, const char*, uint8_t)
/home/shawn/arduino/arduino-0019/libraries/SdFat/SdFat.h:328: note:                 uint8_t SdFile::open(SdFile&, const char*, uint8_t)
/home/shawn/arduino/arduino-0019/libraries/SdFat/SdFat.h:332: note:                 uint8_t SdFile::open(SdFile&, const char*)
/home/shawn/arduino/arduino-0019/libraries/SdFat/SdFat.h:338: note:                 uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)


I thought that perhaps I needed to pass it the pointer reference to the String and I tried using

Code: [Select]

     String filename = "file.txt"
     if (! file.open(&root, filename.toCharArray(), O_READ)) {
         ......
     }


And I get this error

error: no matching function for call to 'String::toCharArray()'
/home/shawn/arduino/arduino-0019/hardware/arduino/cores/arduino/WString.h:80: note: candidates are: void String::toCharArray(char*, unsigned int)

Can someone be kind enough to explain to me what I'm doing wrong and how I can fix this?

Thank you.

davekw7x

#1
Sep 13, 2010, 08:22 am Last Edit: Sep 13, 2010, 10:37 am by davekw7x Reason: 1
Quote
I'm quite new at programming in C/C++

Then either you will hate what I am going to say, or you will embrace the "learning opportunity."

I have done some C++ programming but I'm a newcomer to Arduino (I started with arduino-0018 a couple of months ago).  I have downloaded arduino-0019 and am just now starting to evaluate it, so I may be missing something.  See Footnote.

Anyhow, here's what I found so far:

In arduino-0018, there was no "String" class included in the Arduino core.  We could download a String library, "WString", and import it into our sketches.

In this library there was a String class member function called cstr(), which returned a const pointer to char.  The value of the pointer was the address of the buffer that actually holds the characters of the string.

Then we could use something like
Code: [Select]

String filenameString;  // Or maybe initialize with a C-style string literal
//
// Build up the string by using various String functions and operators.
//
// Then if you have a function that needs to use that string
// but it takes a parameter that is "const char *" you do it like this:
   if (! file.open(&root, filenameString.cstr(), O_READ)) {
       ........
   }


Alas, the developers of arduino-0019, when they rewrote the String class and included it in the Arduino core, chose not to give us the cstr() function.  Go figure.

Anyhow, the way that they did give us is, in my outsider's opinion, really inconvenient and inefficient.  They kept  the toCharArray() function from the previous version.

The function toCharArray() requires us to give it the name of an array of chars and a length parameter.  The size of the array must be large enough to hold all of the characters in the String object plus one more character to hold the terminating zero.   After all of that, the function copies the characters of the String object (and the terminating zero byte) to the array.  So you can do something like
Code: [Select]

   String filenameString; // or initialize it with a "literal"
.
.   // build up the file name string somehow
.
   char filename[100]; // Or something long enough to hold the longest file name you will ever use.
   filenameString.toCharArray(filename, sizeof(filename));
   if (! file.open(&root, filename, O_READ)) {
       ........
   }


Alternatively, you can add a "cstr()" function to arduino-0019/hardware/arduino/core/arduino/WString.h

Find the line that starts the //general methods declarations (It's line 56 in my Linux distribution)

Insert a line as follows
Code: [Select]

   // general methods // <=== this was line 56
   const char * cstr() const {return _buffer;} // This is the new function.
   char charAt( unsigned int index ) const; // <=== this was line 57


Now you should be able to use it like we used to:
Code: [Select]

   if (! file.open(&root, fileameString.cstr(), O_READ)) {
.
.
.



Regards,

Dave

Footnote:
If I am missing something, and there really is a convenient and efficient way that doesn't require modifying the arduino-0019 core library String class, I would appreciate some illumination on the subject.

sholland

#2
Sep 13, 2010, 06:17 pm Last Edit: Sep 13, 2010, 06:29 pm by sholland Reason: 1
Hi Dave,

Thanks for the detailed response. I will have to read over your reply more carefully. For now I was able to accomplish what I needed with:
Code: [Select]

     char filename[11];
     int i = 0;
     while (i <= string.length() -1) {
       if (i>11) {
          i = 11;
          break;
       }
       filename[i] = string.charAt(i);
       i++;
     }
     filename[i] = 0;
     if (! file.open(&root, filename, O_READ)) {
       .......
     }



--
Shawn

sholland

Code: [Select]

     char filename[100];
     filenameString.toCharArray(filename, 100);
     if (! file.open(&root, filename, O_READ)) {
         ......
     }


This is much better. Thank you.

I didn't realize that it took your string and "sent" it to a Char array.. The arduino documentation on this method is misleading.

http://arduino.cc/en/Reference/StringToCharArray

--
Shawn

davekw7x

#4
Sep 13, 2010, 07:04 pm Last Edit: Sep 13, 2010, 07:51 pm by davekw7x Reason: 1
@sholland:
Quote
The arduino documentation on this method is misleading

The wonders of open source:  The source code is available for our edification.  (See Footnote.)
Code: [Select]

void String::toCharArray(char *buf, unsigned int bufsize)
{
 if (!bufsize || !buf) return;
 unsigned int len = bufsize - 1;
 if (len > _length) len = _length;
 strncpy(buf, _buffer, len);
 buf[len] = 0;
}


It just copies the internal buffer contents to the char array that you supply.  Note that part of the description is not just "misleading."  It's flat wrong:
From the description: "Modifying the returned array will also modify the String itself." In fact, that is not true.  Nothing that you change in your array will change anything in the string itself.



Regards,

Dave

"The Source, Luke! Use the Source!"
---Obi-wan Arduino

sholland

Actually modifying the WString.h works more like how I want it to

Thanks again for this information. It's really helped.

Chet

I modified my WString.h and WString.cpp files to make toCharArray match the documentation.  Does anyone see a potential problem with this?

WString.h
Code: [Select]
char* toCharArray();

WString.cpp
Code: [Select]
char* String::toCharArray()
{
     return _buffer;
}

PaulS

Whatever you hand that pointer back to can modify the string. The initial toCharArray() makes a copy of the String contents.

Go Up