I'm getting the text from editbox and I'd want to get each name separated by enter key like the character string below with NULL characters.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5";
while(*names)
{
names += strlen(names)+1;
}
how would you do the same for enter key (i.e separated by /r/n) ? can you do that without using the std::string class?
Use strstr:
while (*names)
{
char *next = strstr(names, "\r\n");
if (next != NULL)
{
// If you want to use the key, the length is
size_t len = next - names;
// do something with a string here. The string is not 0 terminated
// so you need to use only 'len' bytes. How you do this depends on
// your need.
// Have names point to the first character after the \r\n
names = next + 2;
}
else
{
// do something with name here. This version is 0 terminated
// so it's easy to use
// Have names point to the terminating \0
names += strlen(names);
}
}
One thing to note is that this code also fixes an error in your code. Your string is terminated by a single \0, so the last iteration will have names point to the first byte after your string. To fix your existing code, you need to change the value of names to:
// The algorithm needs two \0's at the end (one so the final
// strlen will work and the second so that the while loop will
// terminate). Add one explicitly and allow the compiler to
// add a second one.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5\0";
If you want to start and finish with a C string, it's not really C++.
This is a job for strsep.
#include <stdlib.h>
void split_string( char *multiline ) {
do strsep( &multiline, "\r\n" );
while ( multiline );
}
Each call to strsep zeroes out either a \r or a \n. Since only the string \r\n appears, every other call will return an argument. If you wanted, you could build an array of char*s by recording multiline as it advances or the return value of strsep.
void split_string( char *multiline ) {
vector< char* > args;
do {
char *arg = strsep( &multiline, "\r\n" );
if ( * arg != 0 ) {
args.push_back( arg );
}
} while ( multiline );
}
This second example is at least not specific to Windows.
Here's a pure pointer solution
char * names = "name1\r\nName2\r\nName3";
char * plast = names;
while (*names)
{
if (names[0] == '\r' && names[1] == '\n')
{
if (plast < names)
{
size_t cch = names - plast;
// plast points to a name of length cch, not null terminated.
// to extract the name use
// strncpy(pout, plast, cch);
// pout[cch] = '\0';
}
plast = names+2;
}
++names;
}
// plast now points to the start of the last name, it is null terminated.
// extract it with
// strcpy(pout, plast);
Since this has the C++ tag, the easiest would probably using the C++ standard library, especially strings and string streams. Why do you want to avoid std::string when you're doing C++?
std::istringstream iss(names);
std::string line;
while( std::getline(iss,line) )
process(line); // do process(line.c_str()) instead if you need to
Related
CString str = _T("111\n\n\n222");
How to change the above multiple \n into only one \n?
Cannot use Replace directly, because the number of \n is not fixed
while (str.Replace("\n\n", "\n") > 0)
;
You can use CString::GetBuffer to obtain a buffer that you can modify. The corresponding CString::ReleaseBuffer allows you to specify a new length for the string.
If you want to remove consecutive characters, you can do this easily by simply walking through the string and rewriting its characters. Any time you see a character that you wish to remove, simply don't write it and don't update the end-position of the string.
Here's a general-purpose function to remove some number of consecutive characters from a CString:
void LimitConsecutiveCharacters(CString& str, TCHAR ch, int maxConsecutive = 1)
{
LPTSTR *begin = str.GetBuffer(0);
LPTSTR *end = begin;
int consecutive = 0;
for (LPTSTR *pos = begin; *pos != _T('\0'); ++pos)
{
if (*pos == ch)
{
if (consecutive >= maxConsecutive)
continue;
++consecutive;
}
else
{
consecutive = 0;
}
*end++ = *pos;
}
int newLength = end - begin;
str.ReleaseBuffer(newLength);
}
As you can see above, it keeps a count of how many consecutive values it has seen for the target character. If the maximum number of consecutive characters is reached, then it simply moves to the next loop iteration. Any time it sees some other character, the "consecutive" count resets.
The end tracks the position that is being written to, which might even be the same position you're reading from, if you've not removed any characters. At the end, some simple pointer arithmetic calculates the new string length and calls CString::ReleaseBuffer.
An example invocation would be:
CString str = _T("111\n\n\n222");
LimitConsecutiveCharacters(str, _T('\n'));
You can convert your CString into a std::wstring, use regex_replace and then convert back to CString.
The patterns for the regular expression would be something like:
find what: L"\n+"
replace by: L"\n"
I need to be able to parse the following two strings in my program:
cat myfile || sort
more myfile || grep DeKalb
The string is being saved in char buffer[1024]. What I need to end up with is a pointer to a char array for the left side, and a pointer to a char array for the right side so that I can use these to call the following for each side:
int execvp(const char *file, char *const argv[]);
Anyone have any ideas as to how I can get the right arguments for the execvp command if the two strings above are saved in a character buffer char buffer[1024]; ?
I need char *left to hold the first word of the left side, then char *const leftArgv[] to hold both words on the left side. Then I need the same thing for the right. I have been messing around with strtok for like two hours now and I am hitting a wall. Anyone have any ideas?
I recommend you to learn more about regular expressions. And in order to solve your problem painlessly, you could utilize the Boost.Regex library which provides a powerful regular expression engine. The solution would be just several lines of code, but I encourage you to do it yourself - that would be a good exercise. If you still have problems, come back with some results and clearly state where you were stuck.
You could use std::getline(stream, stringToReadInto, delimeter).
I personally use my own function, which has some addition features baked into it, that looks like this:
StringList Seperate(const std::string &str, char divider, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
return Seperate(str, CV_IS(divider), seperationFlags, whitespaceFunc);
}
StringList Seperate(const std::string &str, CharValidatorFunc isDividerFunc, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
bool keepEmptySegments = (seperationFlags & String::KeepEmptySegments);
bool keepWhitespacePadding = (seperationFlags & String::KeepWhitespacePadding);
StringList stringList;
size_t startOfSegment = 0;
for(size_t pos = 0; pos < str.size(); pos++)
{
if(isDividerFunc(str[pos]))
{
//Grab the past segment.
std::string segment = str.substr(startOfSegment, (pos - startOfSegment));
if(!keepWhitespacePadding)
{
segment = String::RemovePadding(segment);
}
if(keepEmptySegments || !segment.empty())
{
stringList.push_back(segment);
}
//If we aren't keeping empty segments, speedily check for multiple seperators in a row.
if(!keepEmptySegments)
{
//Keep looping until we don't find a divider.
do
{
//Increment and mark this as the (potential) beginning of a new segment.
startOfSegment = ++pos;
//Check if we've reached the end of the string.
if(pos >= str.size())
{
break;
}
}
while(isDividerFunc(str[pos]));
}
else
{
//Mark the beginning of a new segment.
startOfSegment = (pos + 1);
}
}
}
//The final segment.
std::string lastSegment = str.substr(startOfSegment, (str.size() - startOfSegment));
if(keepEmptySegments || !lastSegment.empty())
{
stringList.push_back(lastSegment);
}
return stringList;
}
Where 'StringList' is a typedef of std::vector, and CharValidatorFunc is a function pointer (actually, std::function to allow functor and lambda support) for a function taking one char, and returning a bool. it can be used like so:
StringList results = String::Seperate(" Meow meow , Green, \t\t\nblue\n \n, Kitties!", ',' /* delimeter */, DefaultFlags, is_whitespace);
And would return the results:
{"Meow meow", "Green", "blue", "Kitties!"}
Preserving the internal whitespace of 'Meow meow', but removing the spaces and tabs and newlines surrounding the variables, and splitting upon commas.
(CV_IS is a functor object for matching a specific char or a specific collection of chars taken as a string-literal. I also have CV_AND and CV_OR for combining char validator functions)
For a string literal, I'd just toss it into a std::string() and then pass it to the function, unless extreme performance is required. Breaking on delimeters is fairly easy to roll your own - the above function is just customized to my projects' typical usage and requirements, but feel free to modify it and claim it for yourself.
In case this gives anyone else grief, this is how I solved the problem:
//variables for the input and arguments
char *command[2];
char *ptr;
char *LeftArg[3];
char *RightArg[3];
char buf[1024]; //input buffer
//parse left and right of the ||
number = 0;
command[0] = strtok(buf, "||");
//split left and right
while((ptr=strtok(NULL, "||")) != NULL)
{
number++;
command[number]=ptr;
}
//parse the spaces out of the left side
number = 0;
LeftArg[0] = strtok(command[0], " ");
//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
number++;
LeftArg[number]=ptr;
}
//put null at the end of the array
number++;
LeftArg[number] = NULL;
//parse the spaces out of the right side
number = 0;
RightArg[0] = strtok(command[1], " ");
//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
number++;
RightArg[number]=ptr;
}
//put null at the end of the array
number++;
RightArg[number] = NULL;
Now you can use LeftArg and RightArg in the command, after you get the piping right
execvp(LeftArg[0], LeftArg);//execute left side of the command
Then pipe to the right side of the command and do
execvp(RightArg[0], RightArg);//execute right side of command
Say I have a text file like this:
User: John
Device: 12345
Date: 12/12/12
EDIT:
I have my code to successfully search for a word, and display the info after that word. However when I try to edit the code to search for 2 or 3 words and display the info after them instead of just 1 word, I cannot get it to work. I have tried adding codes into the same while loop, and creating a new while loop for the other word, but both doesn't work. There must be something I am doing wrong/not doing.
Please advice, thanks!
Here is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char file[100];
char c[100];
printf ("Enter file name and directory:");
scanf ("%s",file);
FILE * fs = fopen (file, "r") ;
if ( fs == NULL )
{
puts ( "Cannot open source file" ) ;
exit( 1 ) ;
}
FILE * ft = fopen ( "book5.txt", "w" ) ;
if ( ft == NULL )
{
puts ( "Cannot open target file" ) ;
exit( 1 ) ;
}
while(!feof(fs)) {
char *Data;
char *Device;
char const * rc = fgets(c, 99, fs);
if(rc==NULL) { break; }
if((Data = strstr(rc, "Date:"))!= NULL)
printf(Data+5);
if((Data = strstr(rc, "Device:"))!=NULL)
printf(Device+6);
}
fclose ( fs ) ;
fclose ( ft ) ;
return 0;
}
Ok, hope I can clear it this time. Sorry if I get confusing sometimes but my english is not the best.
I'll explain the implementation inside comments:
#define BUFFSIZE 1024
int main()....
char buff[BUFFSIZE];
char delims[] = " "; /*Where your strtok will split the string*/
char *result = NULL;
char *device; /*To save your device - in your example: 12345*/
char *date; /*To save the date*/
int stop = 0;
fp = fopen("yourFile", "r");
while( fgets(buff, BUFFSIZE,fp) != NULL ) /*This returns null when the file is over*/
{
result = strtok( buff, delims ); /*You just need to do reference to buff here, after this, strtok uses delims to know where to do the next token*/
while(result != NULL){ /*Strtok returns null when finishes reading the given string*/
if(strcmp(result,"Device")==0){ /*strcmp returns 0 if the strings are equal*/
result = strtok(NULL, delims); /*this one gets the 12345*/
device = (char*)malloc((strlen(result)+1)*sizeof(char)); /*Alocate the right amount of memory for the variable device*/
strcpy(device, result); /*Now, device is "12345"*/
}
/*Here you do the same but for the string 'Date'*/
if(strcmp(result,"Date")==0){ /*strcmp returns 0 if the strings are equal*/
result = strtok(NULL, delims); /*this one gets the 12345*/
date = (char*)malloc((strlen(result)+1)*sizeof(char)); /*Alocate the right amount of memory for the variable device*/
strcpy(date, result); /*Now, device is "12/12/12"*/
}
/*And you can repeat the if statement for every string you're looking for*/
result = strtok(NULL,delims); /*Get the next token*/
}
}
/*No strtok necessary here */
...
Hope this helps.
fgetc returns an integer value, which is character, promoted to int.
I suppose you meant fgets which reads a whole line, but you need to reserve memory for it, for example:
#define BUF 100
...
char c[BUF];
fgets(c, BUF, fs);
Some helpful links.
There are a couple of problems in your code: basically it never compiled.
Here is a version with small cleanups - which at least compiles:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char file[100];
char c[100];
printf ("Enter file name and directory:");
scanf ("%s",file);
FILE * fs = fopen (file, "r") ;
if ( fs == NULL ) {
puts( "Cannot open source file" ) ;
exit(1 ) ;
}
while(!feof(fs)) {
char *Data;
char const * rc = fgets(c, 99, fs);
if(rc==NULL) { break; }
if((Data = strstr(rc, "Device"))!= NULL)
printf("%s", Data);
}
fclose ( fs ) ;
return 0;
}
Problems I found:
Missing include for exit()
Missing parameter for exit()
Missing while loop to run through the whole input file.
The output file was never used.
Missing return value of 'main'
Fancy Data[5]
Changed fgetc() to fgets()
I only did minimal edits - it's not perfect at all....
IMHO I would go for C++: many things are much simpler there.
If printf() isn't a hard/fast rule, and the input requirements are really this simple, I'd prefer a state-machine and a constant-memory input:
int c, x = 0; // c is character, x is state
while(EOF!=(c=getchar())){ // scanner entry point
if(c == '\n') x=0; // newline resets scanner
else if(x == -1) continue; // -1 is invalid state
else if (x < 7 && c=="Device:"[x])x++; // advance state
else if (x == 7 && isspace(c)) continue; // skip leading/trailing whitespace
else if (x == 7) putchar(c); // successful terminator (exits at \n)
else x = -1; // otherwise move to invalid state
}
I would do that with two loops: one to get a line from the file and other to make tokens from the line read.
something like:
#define BUFFSIZE 1024
int main()....
char buff[BUFFSIZE];
char delims[] = " ";
char *result = NULL;
int stop = 0;
fp = fopen("yourFile", "r");
while( fgets(buff, BUFFSIZE,fp) != NULL ) /*This returns null when the file is over*/
{
result = strtok( buff, delims ); /*You just need to do reference to buff here, after this, strtok uses delims to know where to do the next token*/
while(result != NULL){ /*Strtok returns null when finishes reading the given string*/
if(strcmp(result,"Device")==0){ /*strcmp returns 0 if the strings are equal*/
stop = 1; /*Update the flag*/
break; /*Is now possible to break the loop*/
}
result = strtok(NULL,delims); /*Get the next token*/
}
if(stop == 1) break; /*This uses the inside flag to stop the outer loop*/
}
result = strtok(NULL, delims); /*Result, now, has the string you want: 12345 */
...
this code is not very accurate and I didn't tested it, but thats how I would try to do it.
Hope this helps.
My suggestion is to use fread to read all the file.You could read it character by character, but IMHO (a personal taste here) it's simpler to get a string containing all the characters and then manipulating it.
This is the function prototype:
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
It returns the number of elements read.
For example:
char buffer[100];
size_t n= fread(buffer, 1,100, fs);
Then you can manipulate the string and divide it in tokens.
EDIT
There is a nice reference with also an example of how dividing a string into tokens here:
http://www.cplusplus.com/reference/cstring/strtok/
c and Data are char-pointers, pointers to (the start of a list of) character value(s).
fgetc's prototype is int fgetc ( FILE * stream ); meaning that it returns (one) integer value (an integer is convertible to a single char value).
If fgetc's prototype would've been int * fgetc ( FILE * stream ); the warning wouldn't have appeared.
#Dave Wang
My answer was too big to be a comment. So here it goes:
You're welcome. Glad to help.
If you make a new loop, the fgets won't work because you are already 'down' in the text file. Imagine something like a pointer to the file, every time you 'fget it' from a file pointer, you advance that pointer. You have functions to reload the file or push that pointer up, but it is not efficient, you've already passed by the information you want, there must be a way to know when.
If you're using my implementation, that is done by using another string compare inside the loop:
if(strcmp(result,"date") == 0)
If you enter this if, you know that the next value in result token with strtok is the actual date.
Since you have now two conditions to be tested, you can't break the outer loop before having both of them. This can be accomplished by two ways:
1-Instead of a flag, use a counter that is incremented everytime you want an information. If that counter has the same number of information you want, you can break the outer loop.
2-Don't break the outer loop at all! :)
But in both, since there are 2 conditions, make sure you treat them inside the ifs so you know that you dealing with the right information.
Hope this helps. Anything, just ask.
I asked this question before but with less information than I have now.
What I essentially have is a data block of type char. That block contains filenames that I need to format and put into a vector. I initially thought the formation of this char block had three spaces between each filename. Now, I realize they are '/0' null terminated characters. So the solution that was provided was fantastic for the example I gave when I thought that there were spaces rather than null chars.
Here is what the structure looks like. Also, I should point out I DO have the size of the character data block.
filename1.bmp/0/0/0brick.bmp/0/0/0toothpaste.gif/0/0/0
The way the best solution did it was this:
// The stringstream will do the dirty work and deal with the spaces.
std::istringstream iss(s);
// Your filenames will be put into this vector.
std::vector<std::string> v;
// Copy every filename to a vector.
std::copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter(v));
// They are now in the vector, print them or do whatever you want with them!
for(int i = 0; i < v.size(); ++i)
std::cout << v[i] << "\n";
This works fantastic for my original question but not with the fact they are null chars instead of spaces. Is there any way to make the above example work. I tried replacing null chars in the array with spaces but that didn't work.
Any ideas on the best way to format this char block into a vector of strings?
Thanks.
If you know your filenames don't have embedded "\0" characters in them, then this should work. (untested)
const char * buffer = "filename1.bmp/0/0/0brick.bmp/0/0/0toothpaste.gif/0/0/0";
int size_of_buffer = 1234; //Or whatever the real value is
const char * end_of_buffer = buffer + size_of_buffer;
std::vector<std::string> v;
while( buffer!=end_of_buffer)
{
v.push_back( std::string(buffer) );
buffer = buffer+filename1.size()+3;
}
If they do have embedded null characters in the filename you'll need to be a little cleverer.
Something like this should work. (untested)
char * start_of_filename = buffer;
while( start_of_filename != end_of_buffer )
{
//Create a cursor at the current spot and move cursor until we hit three nulls
char * scan_cursor = buffer;
while( scan_cursor[0]!='\0' && scan_cursor[1]!='\0' && scan_cursor[2]!='\0' )
{
++scan_cursor;
}
//From our start to the cursor is our word.
v.push_back( std::string(start_of_filename,scan_cursor) );
//Move on to the next word
start_of_filename = scan_cursor+3;
}
If spaces would be a suitable separator, you could just replace the null characters by spaces:
std::replace(std::begin(), std::end(), 0, ' ');
... and go from there. However, I'd suspect that you really need to use the null characters as separators as file names typically can include spaces. In this case, you could either use std::getline() with '\0' as the end of line or use the find() and substr() members of the string itself. The latter would look something like this:
std::vector<std::string> v;
std::string const null(1, '\0');
for (std::string::size_type pos(0); (pos = s.find_first_not_of(null, pos)) != s.npos; )
{
end = s.find(null, pos);
v.push_back(s.substr(0, end - pos));
pos = end;
}
I couldn't find a function which would let me specify the start pos for beginning a char or substring search.
I have, for example:
char *c = "S1S2*S3*S4";
I'd like search for 'S3' by searching the first '*' asterisk and then the second asterisk following it and finally getting the substring 'S3' enclosed by those asterisks.
The string class has a large find family of functions that take an index as a second argument. Repeated applications of find('*', index) should get you what you need.
std::string s(c);
std::string::size_type star1 = s.find('*');
std::string::size_type star2 = s.find('*', star1 + 1);
std::string last_part = s.substr(star2 + 1);
One solution would be to find the location of the first asterisk, then the location of the second asterisk. Then use those positions as the start and end locations to search for S3.
Use
char *strchr( const char *str, int ch );
See here for reference
#include <string>
std::string between_asterisks( const std::string& s ) {
std::string::size_type ast1 = s.find('*');
if (ast1 == std::string::npos) {
throw some_exception();
}
std::string::size_type sub_start = ast1+1;
std::string::size_type ast2 = s.find('*', sub_start);
if (ast2 == std::string::npos) {
throw some_exception();
}
return s.substr(sub_start, ast2-sub_start);
}
You can use strchr(). Simply save the returned pointer and pass it to the next call. As this pointer points to the occurence of your search, the search will start from there.
well one possibility - if you are to use c-style char* arrays for strings - is to use strchr to search for the occurrences of the asterisks, e.g., (and with NO error checking, mind)
char c []= "S1S2*S3*S4";
char* first = strchr(c,'*');
if (first) {
char* start = ++first;
char* nextast = strchr(start,'*');
char* s3str = new char[nextast-start+1];
strncpy(s3str,start,nextast-start);
s3str[next-start] = '\0';
}
But it would be easier to use the C++ string class to do this.