char* oledScreen::getCurrentTime(){
char* hour = malloc(16);
snprintf(hour, 16, "%d", getHour());
char* minute = malloc(16);
snprintf(minute, 16, "%d", getMinute());
char* firstPart = strcat(getHour() < 10 ? strcat("0",hour) : hour, ":");
const char* secondPart = getMinute() < 10 ? strcat("0",minute) : minute;
return strcat(firstPart, secondPart);
};
I'm trying to append two integers, which I can obtain using getHour() and getMinute(). However, I need to check if one of these two are less than 10: if so, I need to append a 0 so that the output is such that: 0X, where X is getHour() or getMinute().
My problem is that it does not append the : character. For instance, if getHour() = 9 and getMinute() = 15. The output of getCurrentTime() is 0915 and not 09:15. Do you have any idea why this is like this?
To begin with, strcat("0",hour) will lead to undefined behavior as you attempt to modify the literal string "0" which isn't allowed.
Instead of using multiple strcat calls, why not simply create a string large enough to fit the result, and use snprintf to put contents into the string?
Using snprintf will also make it easier to optionally add a zero-prefix to the hour value.
Perhaps something like this:
// Allocate memory, and actually create the string
// 2 for hour, 2 for minutes, 1 for colon, 1 for terminator
char *result = malloc(6);
snprintf(result, 6, "%02d:%02d", getHour(), getMinute());
return result;
As mentioned in comments, a better solution would be to pass in the result array as an argument to the function. The would handle ownership of the result array better, and might not even need dynamic allocation.
[Note that the above is a pure C solution, it's not even valid in C++. I wrote it before noticing that the code in the question is really C++.]
Considering that you really seem to be programming in C++, not C, then a solution using string streams and std::string would be more appropriate:
std::string oledScreen::getCurrentTime(){
std::ostringstream oss;
oss << std::setw(2) << std::setfill('0') << getHour() << ':'
<< std::setw(2) << std::setfill('0') << getMinute();
return oss.str();
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have two unsigned char* and trying two concatenate. Here is the sample of code.
unsigned char* finaleResult = (unsigned char*)malloc(size);
memcpy(finaleResult, part1, sizeof(part1));
memcpy(finaleResult+sizeof(part1), part2, sizeof(part2));
finaleResult[sizeof(part1) + sizeof(part2)+1] = '\0';
std::cout << "finaleResult: " << finaleResult << std::endl;
I assume to see two part1 + part2 in the finaleResult but there is only part1.
What is wrong with my code?
If you're using C++, there is no need to do any manual memory management for string-like data and doing manipulations such as concatenation.
The std::basic_string template provides functions that do the concatenation for you. All you have to do is supply the type as a template argument to std::basic_string.
Since the type is unsigned char, using std::basic_string<unsigned char> gives you concatenation right out-of-the-box:
#include <string>
#include <iostream>
int main()
{
std::basic_string<unsigned char> part1 = {'a','b','c'};
std::basic_string<unsigned char> part2 = {'1','2','3'};
std::basic_string<unsigned char> finaleResult = part1 + part2; // magic
std::cout << finaleResult.c_str();
}
Output:
abc123
There are many issues with this code.
unsigned char * is not meant to point at a C string. If you are working with strings in C, you are expected to use plain char *. unsigned char * is generally considered to be pointing at raw data.
Strings
In C, you would use strcpy and strcat to copy / concatenate strings, respectively. Make sure your destination has enough memory malloc'ed to contain the concatenated string, plus the null terminator.
In C++, you do none of the above. No malloc, no memcpy or strcpy or strcat, and no pointers. You #include <string> and use the std::string class.
std::string finaleResult{ part1 };
finaleResult += part2;
std::cout << "finaleResult: " << finaleResult << std::endl;
Data
Later on you confirmed in comments that you are, indeed, handling raw data here. We don't really see what part1 and part2 and size are, so there's some guesswork involved, but I assume that somewhere in part1, in the very least at finalResult[ sizeof( part1 ) ], you got a zero byte, which makes std::cout stop the output.
Similar as with strings (see above), in C++ you should not work with C primitives (arrays, malloc, owning pointers) when handling raw data.
If you have to use dynamic memory allocation, use std::unique_ptr to get a smart pointer that releases the memory upon destruction (e.g. in case of an exception being thrown).
But generally, you will fare better with <vector>, which relieves you from the burden of managing any resources by hand:
vector< unsigned char > finaleResult { part1, part1 + sizeof part1 };
finaleResult.insert( result.end(), part2, part2 + sizeof part2 );
To avoid funny business with embedded zero bytes, characters outside ASCII-7 range, and generally data that isn't supposed to be interpreted as string, use hex output:
int elements { 0 };
int const elements_per_line { 4 };
for ( auto && c : finaleResult )
{
std::cout << std::hex << std::setfill( '0' ) << std::setw( 2 ) << static_cast< int >( c ) << " ";
if ( ( ++elements % elements_per_line ) == 0 )
{
std::cout << std::endl;
elements = 0;
}
}
If I have a string where there is some word and some number (like "Text 125"), is it possible to get this number and convert it to int without using regex?
Yes, you could use a stringstream if you know that it's a word followed by a number.
std::stringstream ss("Text 125");
std::string buffer; //a buffer to read "Text" into
int n; //to store the number in
ss >> buffer >> n; //reads "Text" into buffer and puts the number in n
std::cout << n << "\n";
Edit: I found a way to do it without needing to declare a pointless variable. It's a bit less robust though. This version assumes that there is nothing after the number. std::stoi will work regardless of the number of spaces between the word and the number.
std::string str("Text 125");
int n = std::stoi(str.substr(str.find_first_of(' ') + 1));
std::cout << n << std::endl;
If you know exactly the format of the expected string, i.e. that it
always starts with "Text"
followed by a space
followed by a number
and then ends
(so in regex terms, it's /^Text (\d+)$/)
you can combine std::find and std::substr.
const std::string inputStr = "Text 125";
const std::string textStr = "Text ";
const std::size_t textPos = inputStr.find(textStr);
const std::size_t numberPos = textPos + textStr.length();
const std::string numberStr = inputStr.substr(numberPos);
const int numberInt = std::atoi(numberStr.c_str());
However, that only works in these specific circumstances. And even if /^Text (\d+)$/ is the only expected format, other input strings might still be possible, so you'll need to add the appropriate length checks, and then either throw an exception or return an invalid number or whatever you need to happen for invalid input strings.
#DanielGiger's answer is more generally applicable. (It only requires the number to be the second string, covering the more general case of /^\S+\s+(\d+)/)
As the title said, I'm curious if there is a way to read a C++ string with scanf.
I know that I can read each char and insert it in the deserved string, but I'd want something like:
string a;
scanf("%SOMETHING", &a);
gets() also doesn't work.
Thanks in advance!
this can work
char tmp[101];
scanf("%100s", tmp);
string a = tmp;
There is no situation under which gets() is to be used! It is always wrong to use gets() and it is removed from C11 and being removed from C++14.
scanf() doens't support any C++ classes. However, you can store the result from scanf() into a std::string:
Editor's note: The following code is wrong, as explained in the comments. See the answers by Patato, tom, and Daniel Trugman for correct approaches.
std::string str(100, ' ');
if (1 == scanf("%*s", &str[0], str.size())) {
// ...
}
I'm not entirely sure about the way to specify that buffer length in scanf() and in which order the parameters go (there is a chance that the parameters &str[0] and str.size() need to be reversed and I may be missing a . in the format string). Note that the resulting std::string will contain a terminating null character and it won't have changed its size.
Of course, I would just use if (std::cin >> str) { ... } but that's a different question.
Problem explained:
You CAN populate the underlying buffer of an std::string using scanf, but(!) the managed std::string object will NOT be aware of the change.
const char *line="Daniel 1337"; // The line we're gonna parse
std::string token;
token.reserve(64); // You should always make sure the buffer is big enough
sscanf(line, "%s %*u", token.data());
std::cout << "Managed string: '" << token
<< " (size = " << token.size() << ")" << std::endl;
std::cout << "Underlying buffer: " << token.data()
<< " (size = " << strlen(token.data()) << ")" << std::endl;
Outputs:
Managed string: (size = 0)
Underlying buffer: Daniel (size = 6)
So, what happened here?
The object std::string is not aware of changes not performed through the exported, official, API.
When we write to the object through the underlying buffer, the data changes, but the string object is not aware of that.
If we were to replace the original call: token.reseve(64) with token.resize(64), a call that changes the size of the managed string, the results would've been different:
const char *line="Daniel 1337"; // The line we're gonna parse
std::string token;
token.resize(64); // You should always make sure the buffer is big enough
sscanf(line, "%s %*u", token.data());
std::cout << "Managed string: " << token
<< " (size = " << token.size() << ")" << std::endl;
std::cout << "Underlying buffer: " << token.data()
<< " (size = " << strlen(token.data()) << ")" << std::endl;
Outputs:
Managed string: Daniel (size = 64)
Underlying buffer: Daniel (size = 6)
Once again, the result is sub-optimal. The output is correct, but the size isn't.
Solution:
If you really want to make do this, follow these steps:
Call resize to make sure your buffer is big enough. Use a #define for the maximal length (see step 2 to understand why):
std::string buffer;
buffer.resize(MAX_TOKEN_LENGTH);
Use scanf while limiting the size of the scanned string using "width modifiers" and check the return value (return value is the number of tokens scanned):
#define XSTR(__x) STR(__x)
#define STR(__x) #x
...
int rv = scanf("%" XSTR(MAX_TOKEN_LENGTH) "s", &buffer[0]);
Reset the managed string size to the actual size in a safe manner:
buffer.resize(strnlen(buffer.data(), MAX_TOKEN_LENGTH));
The below snippet works
string s(100, '\0');
scanf("%s", s.c_str());
Here a version without limit of length (in case of the length of the input is unknown).
std::string read_string() {
std::string s; unsigned int uc; int c;
// ASCII code of space is 32, and all code less or equal than 32 are invisible.
// For EOF, a negative, will be large than 32 after unsigned conversion
while ((uc = (unsigned int)getchar()) <= 32u);
if (uc < 256u) s.push_back((char)uc);
while ((c = getchar()) > 32) s.push_back((char)c);
return s;
}
For performance consideration, getchar is definitely faster than scanf, and std::string::reserve could pre-allocate buffers to prevent frequent reallocation.
You can construct an std::string of an appropriate size and read into its underlying character storage:
std::string str(100, ' ');
scanf("%100s", &str[0]);
str.resize(strlen(str.c_str()));
The call to str.resize() is critical, otherwise the length of the std::string object will not be updated. Thanks to Daniel Trugman for pointing this out.
(There is no off-by-one error with the size reserved for the string versus the width passed to scanf, because since C++11 it is guaranteed that the character data of std::string is followed by a null terminator so there is room for size+1 characters.)
int n=15; // you are going to scan no more than n symbols
std::string str(n+1); //you can't scan more than string contains minus 1
scanf("%s",str.begin()); // scanf only changes content of string like it's array
str=str.c_str() //make string normal, you'll have lots of problems without this string
I try to convert a unsigned long into a character string, appending a comma at the end of the it. When compiling and running the test code you can find below, I get the following output:
"1234," "1234"
"1234"
The test code is:
#include <cstdio>
#include <iostream>
int main () {
unsigned long c = 1234;
char ch[50];
char ch1[50];
sprintf(ch, "%lu,", c);
std::cout << "\"" << ch << "\"" << " \"" << c << "\"" << std::endl;
snprintf(ch1, 5, "%s", ch);
std::cout << "\"" << ch1 << "\"" << std::endl;
return 1;
}
As far as I understand, ch should be of length 5, 4 digits plus 1 for the comma.
Do I miss an extra plus one for the termination character?
Cheers!
The size that is passed to snprintf includes the terminating null character. Although it is not printed, it still takes space in the buffer.
You should pass strlen(ch) + 1 instead. Or even better, just sizeof(ch1) will suffice since you want to accommodate the entire result before filling the buffer.
Also make sure that the destination buffer is always of a sufficient size, equal to or greater than the size you pass to snprintf. In your particular case it can hardly happen, but in general you should keep that in mind.
From the Linux manual page:
The functions snprintf() and vsnprintf() write at most size bytes (including the trailing null byte ('\0')) to str.
So yes, you should have a length of 6 to get the comma as well.
The C++ way:
#include <string>
unsigned long int c = 1234;
std::string s = "\"" + std::to_string(c) + ",\"";
std::string t = '"' + std::to_string(c) + ',' + '"'; // alternative
As you wrote, you miss an extra space for the termination character.
The functions snprintf() and vsnprintf() write at most size bytes
(including the terminating null byte ('\0')) to str.
As several people has pointed out, you need to include enough space for the null terminator.
It's always worth checking the result returned from snprintf is what you think it should be, as well.
Lastly, I'd recommend using snprintf(buffer, sizeof(buffer), etc, etc)
Your less likely to get embarrassing results if the 2nd parameter happens to be larger than the actual space you've got available.
how can we store int value in char* as representing character in c++.. for example, i want to store 10..char* p is a character pointer and i want to store 10 as character in that pointer...because i want to write iteration that generates character stream based on integer value.how to do char concatenation with integer(as char) with The similar java code be as:
for(int i=0; i<10; i++)
{
string temp=value+i;//here i want to use char* to represent string in c++
System.out.println(temp);
}
I know you said C++, but you also said char* so i am going to treat it as C. With C, you can't really do concatenation like that. The best way to do it would be to calculate the number of characters required, malloc that, then just store the characters in the char array. And remember to free it when you're done using it! In C, you have to do everything yourself!
I'm a little confused about what you're trying to do, but here's some information that I think will probably help you work it out:
In C++, you should primarily use std::string to hold strings of characters.
In regular C, the convention is to use a char* to hold a list of characters - these char*'s have to be null terminated ending in \0 so that your code knows where to stop printing the string of characters.
Preferring the C++ way, you can concatenate strings with the + operator:
Here's an example:
std::string myString = "H";
myString += "e";
myString += "l";
std::cerr << myString; //prints "Hel" to console.
You can also use a string stream which can mix data types:
std::stringstream ss;
ss << "h" << "e" << 7 << myCustomType << std::endl;
One other thing that's good to know is you can store an integer value in a char and it will work out the ascii representation when you print it.
For example:
char x = 65; //x = capital A.