don't care in scanf - c++

Imagine the following:
you read in a string with scanf() but you only need a few of the datapoints in the string.
Is there an easy way to throw away the extraneous information, without losing the ability to check if the appropriate data is there so you can still reject malformed strings easily?
example:
const char* store = "Get: 15 beer 30 coke\n";
const char* dealer= "Get: 8 heroine 5 coke\n";
const char* scream= "Get: f* beer 10 coke\n";
I want to accept the first string, but forget about the beer because beer is yuckie.
I want to reject the second and third strings because they are clearly not the appropriate lists for the 7/11;
So I was thinking about the following construction:
char* bId = new char[16];
char* cId = new char[16];
int cokes;
sscanf([string here], "Get: %d %s %d %s\n", [don't care], bId, &cokes, cId);
This way I would keep the format checking, but what would I put for [don't care] that doesn't make the compiler whine?
Of course I could just make a variable I don't use later on, but that is not the point of this question. Also checking the left and right side seperately is an obvious solution I am not looking for here.
So, is there a way to not care about but still check the type of a piece of string in scanf and friends?

Use a * as assignment suppression character after %
Example:
sscanf([string here], "Get: %*d %s %d %s\n", bId, &cokes, cId);

Related

swscanf can't read integer value

std::wifstream ifstream("JobList.txt");
ifstream.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
if (!ifstream.is_open()) {
std::cout << "파일을 찾을 수 없습니다!" << std::endl;
return 0;
}
std::wstring s;
wchar_t name[20];
int priority{};
int workingTime{};
int requestTime{};
while (ifstream) {
std::getline(ifstream, s);
swscanf(s.data(), L"%[^',']s, %d, %d, %d", name, &priority, &workingTime, &requestTime);
mRequestArrivationQueue.emplace(name, priority, workingTime, requestTime);
}
ifstream.close();
This is JobList.txt file
Good Boy, 1, 2, 5
도서 대출, 1, 2, 13
swscanf read only first wstring(name), but it doesn't read rest integer values
There is a little error in your code and a terrible (even if common) bad practice.
The error in that the conversion format specifier is [set] and it shall not be followed with a s. Here the format string requires a s character afer the first field (which is impossible) so the conversions stops after decoding the first field. The fix is trivial, remove that offending s (and the useless quotes, thanks to #AdrianMole for his comment):
swscanf(s.data(), L"%[^,], %d, %d, %d", name, &priority, &workingTime, &requestTime);
And the terrible practice is to fail to test the return value of a scanf family function. Had you tested it, you would have immediately found that it was 1 and that only the first field had been decoded.
IMHO, unless you are a C programmer and have used the C io functions for a long time, you should better use a C++ [w]stringstream. The syntax is not easier, but error detection is better...

C++ reading string from memory

I wrote a dll application that is hooked into a process. It works but it ONLY shows the FIRST letter.
I wanted to get the whole string. The string could vary from 2 letters to 32 letters.
//READING MEMORY
HANDLE ExeBaseAddress = GetModuleHandleA(0);
char uNameAddr = *(char*)((char*)ExeBaseAddress + 0x34F01C);
printf("%c \n", uNameAddr);
I also wanted to understand the parts:
*(char*)((char*) //<-- what this is for.
And if it is possible to use this if using multilevel pointers:
char multipoint = *(char*)((char*)ExeBaseAddress + 0x34F01C + 0x123 + 0x321 + 0x20);
UPDATE
I guess something is wrong here:
if(uNameAddr == "omnicient")
cout << "YOU ARE OMNI" << endl;
I used the username name omnicient but it did not cout YOU ARE OMNI. I guess my compare is wrong?
%c displays chars (single characters), %s displays NULL-terminated char*s (strings):
HANDLE ExeBaseAddress = GetModuleHandleA(0);
char *uNameAddr = (char*) ExeBaseAddress + 0x34F01C;
printf("%s \n", uNameAddr);
Notice that I also tidied up the pointer casting, but the important thing is I got rid of the final dereference (* at the front) and assigned it to a char* (pointer) instead of a char.
If your string isn't NULL-terminated (unlikely), you will need to use %.*s and pass the length of your string too.
As for the second part of your question:
*(char*)((char*) ExeBaseAddress + 0x34F01C)
let's break it down. Inside the brackets (therefore the first thing to be evaluated) is this:
(char *) ExeBaseAddress + 0x34F01C
Well that's a C cast (casting the HANDLE to a char*) followed by an addition. In other words, it says "Treat this thing as if it is a pointer to some memory, then look ahead by 0x34F01C bytes of memory" (char is always 1 byte). It is now a pointer to a new position in memory.
Then we get out of the brackets and cast to char* again... needlessly. It could have been:
*((char*) ExeBaseAddress + 0x34F01C)
and finally we dereference (the * at the front), which says "Now tell me what the bit of memory you're pointing to is". But in this case you don't want that, because you want the whole string, not just the first letter (inside printf, it loops along the memory you send it printing each character until it finds a 0, aka \0 aka NULL).
char uNameAddr is a character, you need a list of chars (or char*)
try this instead:
char* name= (char*)((char*)ExeBaseAddress + 0x34F01C);
printf("%s \n", name);
What does *(char*)((char*) mean?
(char*)ExeBaseAddress treat ExeBaseAddress as a pointer to some data of type char
((char*)ExeBaseAddress + 0x34F01C) means add 0x34F01C to the above pointer to offset it by 0x34F01C chars
(char*)((char*)ExeBaseAddress + 0x34F01C) means treat this new address as pointer to some chars
*(char*)((char*)ExeBaseAddress + 0x34F01C) take the contents of the first char at that location
char uNameAddr = *(char*)((char*)ExeBaseAddress + 0x34F01C); means put that character into the char sized variable called uNameAddr.
So basically you had a pointer, you offset it, and then took the first character and printed it.
In the example I gave note how I don't take the firat character, and I put it a pointer variable.
Then I used %s in the printf to make it print out all the chars potnted to by name.

Parsing a string into multiple strings

I have to split a string into several ones. In fact what i need is to parse some input from a file that is in the following format (i9, i9, i2) for example.
i9 means a decimal number as:
(5blankspaces)4567
So i need to retrieve that numbers properly. The width is always fixed so every number must obey that.
A correct instance of that format would be
(5spaces)4567(6spaces)453(1space)2
or
(5spaces)4567(6spaces)45322 (where 22 is the argument for i2 in this case)
The white spaces before the numbers are giving me headache, so i thought i could split every argument into a character array and then convert it to integer since the %d specifier ignores all blank space and i dont know how to use the width as well as ignoring spaces.. (if this can be done, i mean, parsing all to integer please say so!!)
If not.. well, i would need help to parse every string into substring, so far i've done this but it's not working:
while (fgets(buffer, 600, f)) {
sscanf(buffer, "%9[^\n]s %9[^\n]s %2[^\n]s", arg1, arg2, arg3);
....
}
Please, any help would be greatly appreciated!!!
This answer is C. That is why I used the variable name new.
Use strncpy() and terminate the result properly.
char part1[10], part2[10], part3[3];
const char *new = " 4567 45322\n"; /* the line read */
strncpy(part1, new, 9); part1[9] = 0;
strncpy(part2, new+9, 9); part2[9] = 0;
strncpy(part3, new+18, 2); part3[2] = 0;
I suggest you do not try to write multi-language source files.
In C++, use substr(), along with the usual string to integer conversions:
#include <string>
std::string s = " 1234 78922";
std::string s1 = s.substr(0, 9);
std::string s2 = s.substr(9, 9);
std::string s3 = s.substr(18); // or substr(18, 2)
int n1 = std::stoi(s1), n2 = std::stoi(s2), n3 = std::stoi(s3);
Apply the usual length checks where appropriate to validate that the input is indeed in the correct format.

2 Dimensional Char Array to CStrings, where each row is a separate string

I'm reading a multi-dimensional char array from a file
char pszBillToAddress[3][31];
Each row of this array holds a line of an address, and ultimately I need to separate all of the components into separate strings for Address, City, State, and Zip, but for now getting each row into its own CString is my goal. What would be a good way to go about doing this? Use a for loop to append all the characters in a row to a CString?
for (int i = 0; i < 3; ++i)
{
strAddress[i] = pszBillToAddress[i];
}
Assuming that those are truly zero terminated strings. If there's any possibility that they will be filled to the end of the array with characters and the null terminator is missing, you'll need a different approach.
The way this is setup, I am assuming each column is a different line of the address with a c-string of 31 characters max?
In any case, pszBillToAddress[0] (same for [1] and [2]) are already c-strings. If you want them in a single c-string, you could do a few things. Perhaps the easiest is to use a string char x[93]; and use strncat() but this is a "C" way of doing things.
I mean something like this:
char pszBillToAddress[3][31];
char x[93];
*x = '\0'; /* Empty string */
/* Retrieve data here somehow */
strncat(x, pszBillToAddress[0], 31);
strncat(x, pszBillToAddress[1], 31);
strncat(x, pszBillToAddress[2], 31);

sscanf() doesn't recognize format properly

When I use sscanf() in the following code, it is taking the whole line and placing it in the first string for some reason, and I do not see any problems with it. The output from Msg() is coming out like PatchVersion=1.1.1.5 = °?¦§-
The file looks like this (except each is a new line, not sure why it shows as one on StackOverflow)
PatchVersion=1.1.1.5
ProductName=tf
appID=440
Code:
bool ParseSteamFile()
{
FileHandle_t file;
file = filesystem->Open("steam.inf", "r", "MOD");
if(file)
{
int size = filesystem->Size(file);
char *line = new char[size + 1];
while(!filesystem->EndOfFile(file))
{
char *subLine = filesystem->ReadLine(line, size, file);
if(strstr(subLine, "PatchVersion"))
{
char *name = new char[32];
char *value = new char[32];
sscanf(subLine, "%s=%s", name, value);
Msg("%s = %s\n", name, value);
}
else if(strstr(subLine, "ProductName"))
{
char *name = new char[32];
char *value = new char[32];
sscanf(subLine, "%s=%s", name, value);
Msg("%s = %s\n", name, value);
}
}
return true;
}
else
{
Msg("Failed to find the Steam Information File (steam.inf)\n");
filesystem->Close(file);
return false;
}
filesystem->Close(file);
return false;
}
One solution would be to use the (rather underused, in my opinion) character group format specifier:
sscanf(subLine, "%[^=]=%s", name, value);
Also, you should use the return value of sscanf() to verify that you did indeed get both values, before relying on them.
%s is "greedy", i.e. it keeps reading until it hits whitspace (or newline, or EOF). The '=' character is none of these, so sscanf just carries on, matching the entire line for the first %s.
You're probably better off using (for example) strtok(), or a simple character-by-character parser.
From the manpage of scanf, regarding %s:
Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null character ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
%s will read characters until a whitespace is encountered. Since there are no whitespaces before/after the '=' sign, the entire string is read.
Your use of arrays is very poor C++ technique. You might use streams but if you insist on using sscanf and arrays then at least use vector to manage your memory.
You might print out exactly what is in subLine and what Msg does. Is this your own code because I have never heard of FileHandle_t. I do know that it has a method that returns a char* that presumably you have to manage.
Regular expressions are part of the boost library and will soon be in the standard library. They are fairly "standard" and you might do well to use it to parse your line.
(boost::regex or tr1::regex if you have it, VS2008 has it)