Link to home
Start Free TrialLog in
Avatar of dvplayltd
dvplayltdFlag for Bulgaria

asked on

How to split LPCTSTR with MS C 2008?

Dear experts!

I’m programmer with 10+ years in industry, but totally new to MS C 2008 and I need help.
I have a variable and here you a sample value
 LPCTSTR sPicFiles =”C:\Temp\Pic001.tif, C:\Temp\Pic002.tif, C:\Temp\Pic003.tif, C:\Temp\Pic004.tif”

I need a code which to give me follow functions:

1.      To split the sPicFiles to somethink like: sPic[0]= =”C:\Temp\Pic001.tif”, sPic[1]= =”C:\Temp\Pic002.tif” .

2.      The structure should be able to convert to LPCTSTR for single elements

3.      To have options to loop in structure in same order on which it is created

4.      I do not know how much elements I’ll have in structure, or may be I know if I count the comma before create the structure.

Please give me example code.
ASKER CERTIFIED SOLUTION
Avatar of Cong Minh Vo
Cong Minh Vo
Flag of Viet Nam image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
or you can convert it to CString
If you use the Boost library then the code snippet below will split the string directly into a vector array.

Its probably slower to execute than writing a function using _tcstok, but you will end up writing less code yourself and remember the STL vector will be taking care of memory management of the array for you.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to tokenise", boost::is_any_of("\n\t "));


cout << strs[0];
cout << strs[1]; // etc etc

Open in new window


Cheers,
  Chris
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of dvplayltd

ASKER

To Chris

Look great! But seem to me that I should install additional library, is that correct?
Yes, you will need to install the Boost libraries from http://www.boost.org/ and set up your project to use them.

To be honest, in the short term for something as trivial as splitting a string into an array it might be just as quick to write your own code to use _tcstok as minhvc suggests. However in the long term the Boost libraries are a great thing to have around, very powerful and a lot of people use them so they are as solid as third-party libraries get.

Cheers,
  Chris
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
To sara

Well, you answer look better then others. But I use Unicode, then? Please post change for Unicode and have great change to accept you answer.

For Chris - I do not want to include additional libraries (files) which to deploy, for minhvc - may be, but this is link only, not code as Sara did.
To sara

Aaa. std is for C++, while I need C code. Correct?
str::string is C++, so are the Boost libraries. Unless the whole of your application is written in C for some reason, then I would not worry about introducing C++ constructs as Visual Studio 2008 should compile either. Here is a version of Sara's code which works with UNICODE - as the string is now made of TCHAR elements.

 
#include "stdafx.h"
#include <stdlib.h>
#include <Windows.h>
#include <tchar.h>
#include <string>
#include <vector>

int main()
{
	std::basic_string<TCHAR> s = _T("C:\\Temp\\Pic001.tif, C:\\Temp\\Pic002.tif, C:\\Temp\\Pic003.tif, C:\\Temp\\Pic004.tif");
	size_t p1 = 0, p2 = 0;
	s += _T(", ");  // add separator at end
	std::vector<std::basic_string<TCHAR>> spic;
		while ((p2 = s.find(_T(", "), p1)) != std::string::npos)
		{
			 if (p2 > p1)
				  spic.push_back(s.substr(p1, p2-p1));
			 p1 = p2+2;
		}
    return 0;
}

Open in new window


Cheers,
  Chris
yes, both boost and stl is c++.

when using a .cpp fileextension the visual studio would automatically use c++ compiler. so it would not be very difficult to put the split function into a cpp source but call it from c sources.

the same code in pure c is more error-prone:

#define MAX_PIC 100
#define MAX_STR 1024

char s[MAX_STR] ={ '\0' };
size_t len = strlen(s);
char * p1 = NULL, * p2 = s;
char * spic[MAX_PIC] = { 0 };
int i = 0;

strcpy(s, ”C:\\Temp\\Pic001.tif, C:\\Temp\\Pic002.tif, C:\\Temp\\Pic003.tif, C:\\Temp\\Pic004.tif”);
len= strlen(s);

// add separator at end

s[len++] = ','; 
s[len++] = ' '; 

while ((p2 = strstr(p1, ", ")) != NULL)
{
     if (p2 > p1)
          spic[i++]  = p1; // store pointer to substring into array
     *p2 = '\0';           // terminate the sub string
      p1 = p2+2;        // next position
}

Open in new window


the code is not tested.

Sara
N.B. Sara I'm not trying to point grub here - your solution looks the best. I have time on my hands to add the unicode stuff to your code, points should still be yours.

Cheers,
  Chris
Well, I'm really confused. As I say, I'm new to C or C++

My C project send commands to extarnal device (a Stradis card). In the manual are written that I should use C in order to be OK with new version of drivers. It is written that if I use C++ my application will work only with 1 driver version .

I think that in a project I can't mix C and C++ code, right ??? But my files are all .cpp ..then = what is my project - C or C++ ???

P.S: I work on it 2 months and it is 90 % ready and work ... but I still do not know basic of C .. :-( :-( :-(
if using visual studio and have not changed code generation in properties a cpp file will compiled with c++ compiler.

the 'driver' thing is wrong. there are no differences between c and c++ when using external resources/driver  beside of a external "C" directive to add (perhaps).

we all could help you with that.

Sara
i don't think you wer using UNICODE now cause a statement like

LPCTSTR x = "whatever...";

would not compile. the LPCTSTR is const char * if ANSI character set and const wchar_t * if UNICODE (UTF16). the "whatever ..." is
const char *

Open in new window

while L"whatever " would be const wchar_t *.

microsoft switches the mapping of LPCTSTR depending on settings at properties - general - characterset of your project. you could use the TCHAR like tampnic showed if you ever intend to switch between character sets. if not i would go to ansi especially if you think you need c coding.

Sara
note, c is a subset of c++ what means that whatever c library comes with your driver it could be called from either c++ or c.

if the header files that come with the driver are already made professional, they would work for both c and c++. if not, they are only made for access from c but that easily can be corrected by putting extern C" around the include:

extern "C"
{
#include "stradisheader.h"
}

Open in new window


Sara
To minhvc

Look that in the end I'll accept your solution. Could you please correct follow example to Unicode ?

       char *strtok(
         char *strToken,
         const char *strDelimit
      );
      wchar_t *wcstok(
         wchar_t *strToken,
         const wchar_t *strDelimit
      );
      unsigned char *_mbstok(
         unsigned char*strToken,
         const unsigned char *strDelimit
      );

      char string[] = sPicFile; // "A string\tof ,,tokens\nand some  more tokens";
char seps[]   = " ,\t\n";
char *token;

   LPCTSTR sPic[999];

         token = strtok( string, seps );
   while( token != NULL )
   {
      /* While there are tokens in "string" */
      printf( " %s\n", token );
      /* Get next token: */
      token = strtok( NULL, seps );
   }
the strtok or _tcstok for TCHAR strings is not quite suitable for you cause it cannot search for a separator which has two characters. if your sample is right you need a comma+space as separator.

you could use only the comma as separator and then increment the pointer to substring to skip a following space char. but that makes the code not easier.

i strongly recommend using either boost or my stl sample code. if you change the std::string type to std::basic_string<TCHAR> you also automatically have the UNICODE supported.

Sara
tampnic has posted the code which uses std::basic_string<TCHAR>.

Sara
Sara's solution in message ID: 36542689 where she shows some C code to accomplish your task looks better than using a strtok mechanism - she's modifying the original string slightly , inserting null-characters where the delimiters used to be, and returning an array of pointers into the original string. Doesn't require extra memory allocation - I think it's an elegant idea. Here it is in UNICODE format.

#define MAX_PIC 100
	#define MAX_STR 1024

	wchar_t s[MAX_STR] ={ '\0' };
	size_t len = wcslen(s);
	wchar_t * p1 = s, * p2 = s;
	wchar_t * spic[MAX_PIC] = { 0 };
	int i = 0;

	wcscpy(s, L"C:\\Temp\\Pic001.tif, C:\\Temp\\Pic002.tif, C:\\Temp\\Pic003.tif, C:\\Temp\\Pic004.tif");
	len= wcslen(s);

	// add separator at end

	wcscat(s,L", ");; 

	while ( p2 = wcsstr(p1, L", ") )
	{
			if (p2 > p1)  spic[i++]  = p1; // store pointer to substring into array
			*p2 = '\0';           // terminate the sub string
			p1 = p2+sizeof(wchar_t);        // next position
	}

Open in new window


Cheers,
  Chris
in your code using strtok you have the space char included into the separators. that will fail if you ever use file names or folder names that have a space char.

Sara
N.B. I tested the above code and the only changes to Sara's original were to initialise p1 differently to fix a bug and use wide character strings/functions throughout. There are minor improvements which could be arguably made e.g. use Microsoft's ahem "secure" variations of the standard C functions.

The idea behind that code is very nice. If I'm ever forced to write straight C again I will use it immediately. Personally I would use Boost as its set up on my machine, Sara's STL approach works just as well for me. She has explained how you can interface C++ code to a C library with the extern "C" directive. But if you *really need* straight C, I would go for the code in my last message.

Cheers,
   Chris
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Yeee, this is probably bigger discussion from all my 400 questions in last 2 years.  Thanks to all for your time.

First – sorry – my example is not so correct. Here  more precise example of data:

sPic=”C:\Temp\logo001.tif, C:\Temp\logo002.tif, C:\Temp\logo003.tif”

The only separator which I’ll use it is , there will be no space between files. I do not need universal solution , I need solution for this case EXACLTY without add any additional libraries. All this become too theoretically, I need practical solution with minimum change  in installation and dependence of additional libraries. The Stradis card are hardware device, after a week test I release that  one major function of device work unstable because I compile under VC9, while it is design under VC6, you tell me to install additional libraries and to face with additional problems  and support in future – NO THANKS.  And this is only one case in which I need this, there will be no ANY other cases.

 All are welcome to question
https://www.experts-exchange.com/questions/27310275/String-conversation-in-MS-C-2008.html
vc6 also has std::string and std::vector. there is no additional library necessary.

the 'more precise example' you posted has same 'properties' as discussed: it misses the L"" or _T() specifier right-hand, so it only compiles for non-unicode. and it has a comma and a space character between files. the strtok will work nevertheless as long as you have no spaces in file or folder names. vc9 is much more stable than vc6 what is 10 years older. if you have problems with stability it comes from the driver and if you were lucky it only will get evident with vc9. but i wouldn't trust on it.

Sara