How to Peek Ahead at a Character Without Passing that Character? - c++

I am doing an implementation of an expression evaluator where the user can type in a binary number with the 0b prefix. I want to be able to (using a string iterator), peek ahead to see if the next character in the expression after the 0 is a b, and if it is, to not pass by that b character and to go back a character to the 0 at the start of the prefix (something like ungetc). Is there a way to unget a character that has been passed over in a string?
What I've tried:
Token::pointer_type Tokenizer::_get_number( Tokenizer::string_type::const_iterator& currentChar, Tokenizer::string_type const& expression )
{
assert( isdigit( *currentChar ) && "currentChar must pointer to a digit" );
Integer::value_type const MAX_UNSIGNED_D10 = (std::numeric_limits<Integer::value_type>::max()-10)/10;
Integer::value_type accumulator = *currentChar++ - '0';
//Binary Numbers
if( *currentChar == '0' )
{
if( *currentChar++ == 'b' )
{
BinaryInteger::value_type binAccum = _get_binary( currentChar, expression );
return make<BinaryInteger>( binAccum );
}
}
}
Token::pointer_type Tokenizer::_get_number( Tokenizer::string_type::const_iterator& currentChar, Tokenizer::string_type const& expression )
{
assert( isdigit( *currentChar ) && "currentChar must pointer to a digit" );
Integer::value_type const MAX_UNSIGNED_D10 = (std::numeric_limits<Integer::value_type>::max()-10)/10;
Integer::value_type accumulator = *currentChar++ - '0';
std::stringstream iss( expression );
//Binary Numbers
if( iss.get() == '0' )
{
if( iss.get() == 'b' )
{
BinaryInteger::value_type binAccum = _get_binary( currentChar, expression );
return make<BinaryInteger>( binAccum );
}
}
}
Also have tried using [] access for the string expression's characters itself but that is extremely limiting to certain cases.

When you call the ++ operator on an iterator, it advances the iterator to the next element. To peek at the next element without advancing the iterator, you can use +1 instead, eg:
if( *currentChar == '0' )
{
if( *(currentChar+1) == 'b' )
{
BinaryInteger::value_type binAccum = _get_binary( currentChar+2, expression );
return make<BinaryInteger>( binAccum );
}
}
Just be careful if currentChar is already at the end of the string before you peek. The next element after the current one will be the string's end position, and you should not dereference that iterator value. You might want to consider adding an extra parameter to your tokenizer so it can detect when it reaches the end of the input string and does not iterate too far.

I believe stringstream peek is what you need (it's inherited from istream, just like unget. Stringstraems are streams working with string. They work the same as file streams and the default I/O streams like cin and cout.

Related

Efficient means of null terminating an unsigned char buffer in a string append function?

I've been writing a "Byte Buffer" utility module - just a set of functions for personal use in low level development.
Unfortunately, my ByteBuffer_Append(...) function doesn't work properly when it null terminates the character at the end, and/or adds extra room for the null termination character. One result, when this is attempted, is when I call printf() on the buffer's data (a cast to (char*) is performed): I'll get only a section of the string, as the first null termination character within the buffer will be found.
So, what I'm looking for is a means to incorporate some kind of null terminating functionality within the function, but I'm kind of drawing a blank in terms of what would be a good way of going about this, and could use a point in the right direction.
Here's the code, if that helps:
void ByteBuffer_Append( ByteBuffer_t* destBuffer, uInt8* source, uInt32 sourceLength )
{
if ( !destBuffer )
{
puts( "[ByteBuffer_Append]: param 'destBuffer' received is NULL, bailing out...\n" );
return;
}
if ( !source )
{
puts( "[ByteBuffer_Append]: param 'source' received is NULL, bailing out...\n" );
return;
}
size_t byteLength = sizeof( uInt8 ) * sourceLength;
// check to see if we need to reallocate the buffer
if ( destBuffer->capacity < byteLength || destBuffer->length >= sourceLength )
{
destBuffer->capacity += byteLength;
uInt8* newBuf = ( uInt8* ) realloc( destBuffer->data, destBuffer->capacity );
if ( !newBuf )
{
Mem_BadAlloc( "ByteBuffer_Append - realloc" );
}
destBuffer->data = newBuf;
}
uInt32 end = destBuffer->length + sourceLength;
// use a separate pointer for the source data as
// we copy it into the destination buffer
uInt8* pSource = source;
for ( uInt32 iBuffer = destBuffer->length; iBuffer < end; ++iBuffer )
{
destBuffer->data[ iBuffer ] = *pSource;
++pSource;
}
// the commented code below
// is where the null termination
// was happening
destBuffer->length += sourceLength; // + 1;
//destBuffer->data[ destBuffer->length - 1 ] = '\0';
}
Many thanks to anyone providing input on this.
Looks like your issue is caused by memory corruption.
You have to fix the following three problems:
1 check if allocated space is enough
if ( destBuffer->capacity < byteLength || destBuffer->length >= sourceLength )
does not properly check if buffer reallocation is needed,
replace with
if ( destBuffer->capacity <= destBuffer->length+byteLength )
2 allocating enough space
destBuffer->capacity += byteLength;
is better to become
destBuffer->capacity = destBuffer->length + byteLength + 1;
3 properly null terminating
destBuffer->data[ destBuffer->length - 1 ] = '\0';
should become
destBuffer->data[ destBuffer->length ] = '\0';
In C/C++, a list of chars terminating by a '\0' is a string. There are a set of string functions, such as strcpy(), strcmp(), they take char * as parameter, and when they find a '\0', they the string end there. In your case, printf("%s", buf) treats buf as a string, so when it find a '\0', it stops print.
If you are doing a buffer, that means any data include '\0' is normal data in the buffer. So you should avoid to use string functions. To print a buffer, you need to implement your own function.

How to add a question mark to the end of a line?

I want to check to see if the user added a ? to the end of the buffer. If not, I want the program to add one automatically. This is what I have so far. I dont know what to do next.
First I check to see if the buffer is not blank.
Then, if the last item is not a ?, add the question mark automatically to the buffer and then copy the content to the current data node.
if ( strlen(buffer) != 0)
{
if (buffer[strlen(buffer)-1] != '?')
{
//what do i put here to add the ? if theres non?
}
strcpy(current->data,buffer);
}
From what I can see, you don't gain anything from modifying buffer in this way. You can simply add the ? to current->data if it is needed.
int len = strlen(buffer);
strcpy(current->data, buffer);
if (len && buffer[len-1] != '?') {
current->data[len] = '?';
current->data[len+1] = '\0';
}
If it is an option, you should consider changing your code to use std::string instead.
std::string buffer = input();
if (!buffer.empty() && buffer.back() != '?') buffer += '?';
std::copy(buffer.begin(), buffer.end(), current->data);
current->data[buffer.size()] = '\0';
If you don't have a C++11 compiler, use *buffer.rbegin() instead of buffer.back().
Why not create a function that checks whether or not the last character is a question mark before you concatenate the question mark?
//Create function that returns a bool
bool isQuestionMark(char * buffer)
{
//Create pointer to buffer
char * pointer = buffer;
//Go to the null character
while(*pointer != '\0')
pointer++;
//Get to the last character
pointer--;
//Check to see if last character is a question mark
if(*pointer == '?')
return true;
else
return false;
}
Then you want to call that function to see if you need to concatenate a question mark.
if(isQuestionMark(buffer) == true)
strcat(buffer, "?");
else
//Do nothing

How do I examine the first character of a QString?

I want the following code to remove a leading zero from a price (0.00 should be cut to .00)
QString price1 = "0.00";
if( price1.at( 0 ) == "0" ) price1.remove( 0 );
This gives me the following error: "error: conversion from ‘const char [2]’ to ‘QChar’ is ambiguous"
The main issue is that Qt is seeing "0" as a null-terminated ASCII string, hence the compiler message about const char[2].
Also, QString::remove() takes two arguments. So you code should be:
if( price1.at( 0 ) == '0' ) price1.remove( 0, 1 );
This builds and runs on my system (Qt 4.7.3, VS2005).
Try this:
price1.at( 0 ) == '0' ?
The problem is that the 'at' function returns a QChar which is an object that can't be compared to the native char/string "0". You have a few choices, but I'll just put two here:
if( price1.at(0).toAscii() == '0')
or
if( price1.at(0).digitValue() == 0)
digitValue returns -1 if the char is not a digit.
QString s("foobar");
if (s[0]=="f") {
return;
}
QChar QString::front() const Returns the first character in the
string. Same as at(0).
This function is provided for STL compatibility.
Warning: Calling this function on an empty string constitutes
undefined behavior.
http://doc.qt.io/qt-5/qstring.html#front
QString s("foobar");
/* If string is not empty or null, check to see if the first character equals f */
if (!s.isEmpty() && s.front()=="f") {
return;
}

C++ compare to string dates

I need to compare 2 string dates to see if one date is later then another. The date format for both dates is at the bottom. i can rearrange this for what ever is easiest. I have boost but it doesn't have to be, ive been through so many examples and can't seem to wrap my brain around getting it to work. Thanks in advance basically i want
2012-12-06 14:28:51
if (date1 < date2) {
// do this
}
else {
// do that
}
It looks like the date format your using is already in lexicographical order and a standard string comparison will work, something like:
std::string date1 = "2012-12-06 14:28:51";
std::string date2 = "2012-12-06 14:28:52";
if (date1 < date2) {
// ...
}
else {
// ...
}
You will need to make sure that spacing and punctuation is consistent when using this format, in particular something like 2012-12-06 9:28:51 will break the comparison. 2012-12-06 09:28:51 will work though.
You're lucky - your dates are already in the proper format to do a standard string comparison and get the correct results. All the parts go from most significant to least significant, and you're using 24-hour times.
If these are std::strings you can use the < just as you have in your sample. If they're C-style character array strings, use strcmp.
strcmp() returns an integral value indicating the relationship between the strings:
result = strcmp( string1, string2 );
if( result > 0 ) strcpy( tmp, "greater than" );
else if( result < 0 ) strcpy( tmp, "less than" );
A zero value indicates that both strings are equal.
A value greater than zero indicates that the first character that does not match has a greater value in str1 than in str2; And a value less than zero indicates the opposite.
#include <string.h>
#include <stdio.h>
char string1[] = "2012-12-06 14:28:51";
char string2[] = "2011-12-06 14:28:51";
int main( void )
{
char tmp[20];
int result;
printf( "Compare strings:\n %s\n %s\n\n\n", string1, string2 );
result = strcmp( string1, string2 );
if( result > 0 ) strcpy( tmp, "greater than" );
else if( result < 0 ) strcpy( tmp, "less than" );
else strcpy( tmp, "equal to" );
printf( " strcmp: String 1 is %s string 2\n\n", tmp );
return 0;
}

Splitting the string at Enter key

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