Build string as in printf function - c++

printf("%d.%d.%d", year, month, day);
Can I do the same but without printing, smth like
char* date = "%d.%d.%d", year, month, day;
Or maybe some other simple ways to do that?

In plain c there is asprintf() which will allocate memory to hold the resulting string:
#include <stdio.h>
char *date;
asprintf(&date, "%d.%d.%d", year, month, day);
(error handling omitted)
Since you have tagged C++ you probably want to use the C++ solutions.

In C++:
#include <string>
std::string date = std::to_string(year) + '.' +
std::to_string(month) + '.' + std::to_string(day);
If you need the underlying char const *, say date.c_str().
The function std::to_string uses snprintf internally; you should probably look up that function, too, as it is fairly fundamental to formatted output, and you can use it directly if you really think you need to.

There are various implementations of a format function that looks something like:
std::string format(const std::string& fmt, ...);
so your example would be:
std::string date = format("%d.%d.%d", year, month, day);
One possible implementation is shown below.
Boost has a format library that works a little differently. It assumes you like cin, cout, and their ilk:
cout << boost::format("%1%.%2%.%3%") % year % month % day;
Or, if you just wanted a string:
boost::format fmt("%1%.%2%.%3%");
fmt % year % month % day;
std::string date = fmt.str();
Note that % flags are not the ones you're used to.
Finally, if you want a C string (char*) instead of a C++ string, you could use the asprintf function:
char* date;
if(asprintf(&date, "%d.%d.%d", year, month, day) == -1)
{ /* couldn't make the string; format was bad or out of memory. */ }
You could even use vasprintf to make your own format function returning a C++ string:
std::string format(const char* fmt, ...)
{
char* result = 0;
va_list ap;
va_start(ap, fmt);
if(vasprintf(*result, fmt, ap) == -1)
throw std::bad_alloc();
va_end(ap);
std::string str_result(result);
free(result);
return str_result;
}
This isn't terribly efficient, but it works. There also might be a way to call vsnprintf twice, the first with no buffer to get the formatted string length, then allocate the string object with the right capacity, then call the second time to get the string. This avoids allocating the memory twice, but has to make two passes through the formatted string.

In C++ I wrote a function to create strings using the printf format.
Headerfile stringf.h:
#ifndef STRINGF_H
#define STRINGF_H
#include <string>
template< typename... argv >
std::string stringf( const char* format, argv... args ) {
const size_t SIZE = std::snprintf( NULL, 0, format, args... );
std::string output;
output.resize(SIZE+1);
std::snprintf( &(output[0]), SIZE+1, format, args... );
return std::move(output);
}
#endif
Usage:
#include "stringf.h"
int main(){
int year = 2020;
int month = 12;
int day = 20
std::string date = stringf("%d.%d.%d", year, month, day);
// date == "2020.12.20"
}

In C language use sprintf function from stdio.h header file.
char buffer[100];
sprintf(buffer,"%d.%d.%d", year, month, day);
See here for more info.

Related

Get System Date Format String Win32

I need to get the system current date format string like ("dd-mm-yyyy" , "mm/dd/yyyy" ect.
GetDateFormat() API returns the formatted string like "12-09-2015" but need string like "dd-mm-yyyy"
C# solution
string sysFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
But I need in Win32.
You can get a list of format strings currently applicable, by enumerating them. This is done through EnumDateFormats. Note, that there can be (and usually is) more than one, so you will have to decide, which one to pick1).
The following code returns the system's default short dateformat pattern:
static std::list<std::wstring> g_DateFormats;
BOOL CALLBACK EnumDateFormatsProc( _In_ LPWSTR lpDateFormatString ) {
// Store each format in the global list of dateformats.
g_DateFormats.push_back( lpDateFormatString );
return TRUE;
}
std::wstring GetShortDatePattern() {
if ( g_DateFormats.size() == 0 &&
// Enumerate all system default short dateformats; EnumDateFormatsProc is
// called for each dateformat.
!::EnumDateFormatsW( EnumDateFormatsProc,
LOCALE_SYSTEM_DEFAULT,
DATE_SHORTDATE ) ) {
throw std::runtime_error( "EnumDateFormatsW" );
}
// There can be more than one short date format. Arbitrarily pick the first one:
return g_DateFormats.front();
}
int main() {
const std::wstring strShortFormat = GetShortDatePattern();
return 0;
}
1) The .NET implementation does the same thing. From its list of candidates, it arbitrarily picks the first one.
You can use time function together with localtime function.
Example code:
//#include <time.h>
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
//The years are since 1900 according the documentation, so add 1900 to the actual year result.
char cDate[255] = {};
sprintf(cDate, "Today is: %d-%d-%d", timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year + 1900);

Sscanf() Function

What is the difference between using '&' with year and day as an arguments and doesn't use it with month and weekday in sscanf() function ?
#include <stdio.h>
#include <stdlib.h>
int main()
{
int day, year;
char weekday[20], month[20], dtm[100];
strcpy( dtm, "Saturday March 25 1989" );
sscanf( dtm, "%s %s %d %d", weekday, month, &day, &year );
printf("%s %d, %d = %s\n", month, day, year, weekday );
return(0);
}
The & operator gets the address of a variable. So you're actually passing a pointer to day and year into sscanf.
Since they are arrays, weekday and month are already pointers to their first elements.
So rather than passing in different things to sscanf you're actually passing in the same things.
2 char*s and 2 int*s.

Forwarding variadic template parameter to printf-like function

I have the following log function:
template<typename... Arguments>
void Log(const char* file, const int line, int level, const char* fmt, Arguments... args)
{
std::string formattedFile;
if (file)
{
boost::filesystem::path p(file);
formattedFile = p.filename().string();
}
std::string message{boost::str(boost::format("%1%:%2% [%3%] - %s") % formattedFile % line % m_uxid % fmt)};
__android_log_print(level, m_tag.c_str(), message.c_str(), args...);
}
This application is run on Android using NDK, so this is the logging system for that platform. The problem is that __android_log_print() fails to compile with:
error: format not a string literal and no format arguments [-Werror=format-security]
__android_log_print(level, m_tag.c_str(), message.c_str(), std::forward<Arguments>(args)...);
^
I'm not sure what this means. Am I not using the variadic template argument correctly?
Untrusted input into printf can be a security problem. Enforcing the format by using a string literal is one way of improving security
Turning warnings into errors will cause the build to fail so you are forced to address the warning.
GCC's warning options have this to say
-Werror:
Make all warnings into errors.
-Wformat-security:
Warn about uses of format functions that represent possible security problems.
At present, this warns about calls to printf and scanf functions where the format string is not a string literal and there are no format arguments
This may be a security hole if the format string came from untrusted input and contains %n.
What is generally advised is to create a std::string in your function and pass this with a %s format string literal to your logging function
__android_log_print(level, m_tag.c_str(), "%s", message.c_str());
Where message is built from processing args..., typically using something like boost::format or a std::stringstream.
If you want to use your provided fmt string and the variadic args, you can parse the arguments using a custom printf style function which produces a std::string
std::string va_string_printf(const char* format, va_list ap)
{
char stack_buf[256];
char* buf = stack_buf;
int buf_size = sizeof(stack_buf);
std::string out_str;
while(true)
{
va_list ap1;
va_copy(ap1, ap);
int min_buf_size = vsnprintf(buf, buf_size, format, ap1) + 1;
va_end(ap1);
if (min_buf_size > buf_size)
{
if (buf != stack_buf) // allocate a bigger buffer
delete[] buf;
buf = new char[min_buf_size];
buf_size = min_buf_size;
continue;
}
out_str = buf;
break;
}
if (buf != stack_buf)
delete[] buf;
return out_str;
}
std::string string_printf(const char* format, ...)
{
va_list ap;
va_start(ap, format);
std::string str = va_string_printf(format, ap);
va_end(ap);
return str;
}

C++ get hour and minutes from string

I'm writing C++ code for school in which I can only use the std library, so no boost. I need to parse a string like "14:30" and parse it into:
unsigned char hour;
unsigned char min;
We get the string as a c++ string, so no direct pointer. I tried all variations on this code:
sscanf(hour.c_str(), "%hhd[:]%hhd", &hours, &mins);
but I keep getting wrong data. What am I doing wrong.
As everyone else has mentioned, you have to use %d format specified (or %u). As for the alternative approaches, I am not a big fan of the "because C++ has feature XX it must be used" and oftentimes resort to C-level functions. Though I never use scanf()-like stuff as it got its own problems. That being said, here is how I would parse your string using strtol() with error checking:
#include <cstdio>
#include <cstdlib>
int main()
{
unsigned char hour;
unsigned char min;
const char data[] = "12:30";
char *ep;
hour = (unsigned char)strtol(data, &ep, 10);
if (!ep || *ep != ':') {
fprintf(stderr, "cannot parse hour: '%s' - wrong format\n", data);
return EXIT_FAILURE;
}
min = (unsigned char)strtol(ep+1, &ep, 10);
if (!ep || *ep != '\0') {
fprintf(stderr, "cannot parse minutes: '%s' - wrong format\n", data);
return EXIT_FAILURE;
}
printf("Hours: %u, Minutes: %u\n", hour, min);
}
Hope it helps.
Your problem is, of course, that you are using sscanf. And that
you're using some very special type for the hours and minutes, instead
of int. Since you're parsing a string of exactly 5 characters, the
simplest solution is just to ensure that all of the characters are legal
in that position, using isdigit for characters 0, 1, 3 and 4, and
comparing to ':' for character 2. Once you've done that, it's trivial
to create an std::istringstream from the string, and input into an
int, a char (which you'll ignore afterwards) and a second int. If
you want to be more flexible in the input, for example allowing things
like "9:45" as well, you can skip the initial checks, and just input
into int, char and int, then check that the char contains ':'
(and that the two int are in range).
As to why your sscanf is failing: you're asking it to match something
like "12[:]34", which is not what you're giving it. I'm not sure
whether you're trying to use "%hhd:%hhd", or if for some reason you
really do want a character class, in which case, you have to use [ as
a conversion specifier, and then ignore the input: "%hhd%*[:]%hhd".
(This would allow accepting more than one character as the separator,
but otherwise, I don't see the advantage. Also, technically at least,
using %d and then passing the address of an unsigned integral types
is not supported, %hhd must be a signed char. In practice,
however, I don't think you'll ever run into any problems for
non-negative input values less than 128.)
As mentioned by izomorphius sscanf and variants are not C++ they are C. The C++ way would be to use streams. The following works (it's not amazingly flexible but should give you an idea)
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main(int argc, char* argv[])
{
string str = "14:30";
stringstream sstrm;
int hour,min;
sstrm << str;
sstrm >> hour;
sstrm.get(); // get colon
sstrm >> min;
cout << hour << endl;
cout << min << endl;
return 0;
}
You could also use getline to get everything upto the colon.
I would do it like this
unsigned tmp_hour, tmp_mins;
unsigned char hour, mins;
sscanf(hour.c_str(), "%u:%u", &tmp_hours, &tmp_mins);
hour = tmp_hours;
mins = tmp_mins;
Less messing around with obscure scanf options. I would add some error checking too.
My understanding is that h in %hhd is not a valid format specifier. The correct specifier for decimal integers is %d.
As R.Martinho Fernandes says in his comment, %d:%d will match two numbers separated by a colon (':').
Did you want something different?
You can always read the entire text string and parse it any way you want.
sscanf with %hhd:%hhd seems to work perfectly fine:
std::string time("14:30");
unsigned char hour, min;
sscanf(time.c_str(), "%hhd:%hhd", &hour, &min);
Note that the hh length modifier is simply to allow storing the value in an unsigned char.
However, sscanf is from the C Standard Library and there are better C++ ways to do this. A C++11 way to do this is using stoi:
std::string time("14:30");
unsigned char hour = std::stoi(time);
unsigned char min = std::stoi(time.substr(3));
In C++03, we can use stringstream instead but it's a bit of a pain if you really want it in a char:
std::stringstream stream("14:30");
unsigned int hour, min;
stream >> hour;
stream.ignore();
stream >> min;

Splitting a `char[]` in C++

I have a char[] in YYYYMMDDHHMMSS format.
e.g. 2011052504572
I want to retrieve the year, month, date, hour, minute and second from this char. How do I do that?
NOTE:I cant use any third party dll.
Thanks,
Syd
If you're using the STL then just put the string into a std::string and use the substr method:
std::string dateTime=......;
std::string year=dateTime.substr(0,4);
std::string month=dateTime.substr(4,2);
// etc
// etc
Use string::substr() for this purpose. Example,
string date = "20110524112233";
string year = date.substr(0, 4);
string month = date.substr(4, 2);
string day = date.substr(6, 2);
string hour = date.substr(8, 2);
string minute = date.substr(10, 2);
string second = date.substr(12, 2);
It depends on whether you want to extract the values as text, or convert them to numbers. For getting lots of strings, you can use std::string and substr() as thoroughly illustrated in other answers.
If you want to get numbers that you can then calculate with, then one approach is:
int year, month, day, hour, minute, second;
if (sscanf(input, "%.4d%.2d%.2d%.2d%.2d%.2d",
&year, &month, &day, &hour, &minute, &second) == 6)
{
// all 6 conversions worked... can use the values...
int second_in_day = hour * 3600 + minute * 60 + second;
...
}
Another approach is to use strptime() - if your system has it. It parses a string into a broken-down-time structure:
struct tm tm;
strptime(input, "%Y%m%d%H%M%S", &tm);
// parsed values are in tm.tm_year, tm.tm_mon, tm.tm_mday,
// tm.tm_hour, tm.tm_min, tm.tm_sec
// further, tm_wday has day of week, tm_yday has day in year
// i.e. it actually understands the date, not just chopping up numbers/text
Note: sscanf() and strncpy() are C functions callable from C++, and they're not as safe to use as C++-specific functionality (std::string, std::istringstream) in that small misunderstandings and mistakes in handling the data can lead to not just erroneous results, but program crashes. So, read the manual pages for these things carefully if you use them.
Use strncpy... okay, so if it is not homework, then this is the best way. The others using std::string are wasting resources:
static const char *yyyymmddhhmmss = "20110526101559";
char year[5] = { '\0' };
char month[3] = { '\0' };
char day[3] = { '\0' };
char hour[3] = { '\0' };
char minute[3] = { '\0' };
char second[3] = { '\0' };
strncpy(year, yyyymmddhhmmss, 4);
strncpy(month, &yyyymmddhhmmss[4], 2);
strncpy(day, &yyyymmddhhmmss[6], 2);
strncpy(hour, &yyyymmddhhmmss[8], 2);
strncpy(minute, &yyyymmddhhmmss[10], 2);
strncpy(second, &yyyymmddhhmmss[12], 2);
Since you have the input in the form of a char[] (or char*—for
this use, it comes out to the same thing), the simplest solution is
probably using the two iterator constructors for std::string to creat
a string for each field, e.g.:
std::string year ( date , date + 4 );
std::string month( date + 4, date + 6 );
std::string day ( date + 6, date + 8 );
// ...
If you need the numerical values, boost::lexical_cast can be used,
e.g.:
int extractField( char const* string, int begin, int end )
{
return boost::lexical_cast<int>(
std::string( date + begin, date + end ) );
}
int year = extractField( date, 0, 4 );
int year = extractField( date, 4, 6 );
// ...