I try to use firebase db,
I found very important restrictions, which are not described in firebase help or FAQ.
First problem is that symbol: dot '.' prohibited in keys,
i.e. firebase reject (with unknown reason) next:
nameRef.child('Henry.Morgan#caribbean.sea').set('Pirat');
Second problem with forward slashes in your keys '/',
when you try to add key like this
{'02/10/2013': true}
In firebase you can see:
'02': {
'10': {
'2013': true
}
}
Have you got any ideas how to solve it (automatically)?
May be set some flag that it is string key with all symbols?
Of course, I can parse/restore data every time before write and after read, but...
By the way '.' '/' - all restricted symbols for firebase ?
The reason that adding a child 02/10/2013 creates a structure in Firebase is because the forward slashes are resulting in the creation of a new level.
So the line I assume you are using something similar to: firebaseRef.child('02/10/2013').set(true) is equivalent to firebaseRef.child('02').child('10').child('2013').set(true).
To avoid the problems of not being able to use the following characters in reference key names (source),
. (period)
$ (dollar sign)
[ (left square bracket)
] (right square bracket)
# (hash or pound sign)
/ (forward slash)
we can use one of JavaScript's built in encoding functions since as far as I can tell, Firebase does not provide a built in method to do so. Here's a run-through to see which is the most effective for our purposes:
var forbiddenChars = '.$[]#/'; //contains the forbidden characters
escape(forbiddenChars); //results in ".%24%5B%5D%23/"
encodeURI(forbiddenChars); //results in ".%24%5B%5D%23%2F"
encodeURIComponent(forbiddenChars); //results in ".%24%5B%5D%23%2F"
Evidently, the most effective solution is encodeURIComponent. However, it doesn't solve all our problems. The . character still poses a problem as shown by the above test and trying to encodeURIComponent your test e-mail address. My suggestion would be to chain a replace function after the encodeURIComponent to deal with the periods.
Here's what the solution would look like for your two example cases:
encodeURIComponent('Henry.Morgan#caribbean.sea').replace(/\./g, '%2E') //results in "Henry%2EMorgan%40caribbean%2Esea"
encodeURIComponent('02/10/2013'); //results in "02%2F10%2F2013"
Since both the final results are safe for insertion into a Firebase as a key name, the only other concern is decoding after reading from a Firebase which can be solved with replace('%2E', '.') and a simple decodeURIComponent(...).
I faced the same problem myself, and I have created firebase-encode for this purpose.
Unlike the chosen answer, firebase-encode encodes only unsafe characters (./[]#$) and % (necessary due to how encoding/decoding works).
It leaves other special characters that are safe to be used as firebase key while encodeURIComponent will encode them.
Here's the source code for details:
// http://stackoverflow.com/a/6969486/692528
const escapeRegExp = (str) => str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
const chars = '.$[]#/%'.split('');
const charCodes = chars.map((c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
const charToCode = {};
const codeToChar = {};
chars.forEach((c, i) => {
charToCode[c] = charCodes[i];
codeToChar[charCodes[i]] = c;
});
const charsRegex = new RegExp(`[${escapeRegExp(chars.join(''))}]`, 'g');
const charCodesRegex = new RegExp(charCodes.join('|'), 'g');
const encode = (str) => str.replace(charsRegex, (match) => charToCode[match]);
const decode = (str) => str.replace(charCodesRegex, (match) => codeToChar[match]);
I wrote this for Java (since I came here expecting a java implementation):
public static String encodeForFirebaseKey(String s) {
return s
.replace("_", "__")
.replace(".", "_P")
.replace("$", "_D")
.replace("#", "_H")
.replace("[", "_O")
.replace("]", "_C")
.replace("/", "_S")
;
}
public static String decodeFromFirebaseKey(String s) {
int i = 0;
int ni;
String res = "";
while ((ni = s.indexOf("_", i)) != -1) {
res += s.substring(i, ni);
if (ni + 1 < s.length()) {
char nc = s.charAt(ni + 1);
if (nc == '_') {
res += '_';
} else if (nc == 'P') {
res += '.';
} else if (nc == 'D') {
res += '$';
} else if (nc == 'H') {
res += '#';
} else if (nc == 'O') {
res += '[';
} else if (nc == 'C') {
res += ']';
} else if (nc == 'S') {
res += '/';
} else {
// this case is due to bad encoding
}
i = ni + 2;
} else {
// this case is due to bad encoding
break;
}
}
res += s.substring(i);
return res;
}
Character limitations are documented at https://www.firebase.com/docs/creating-references.html - you cannot use '.', '/', '[', ']', '#', and '$' in key names. There is no automatic way of escaping these characters, I'd recommend avoiding their use altogether or creating your own escaping/unescaping mechanism.
If you're using Swift 3, this works for me (try it in a playground):
var str = "this.is/a#crazy[string]right$here.$[]#/"
if let strEncoded = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics) {
print(strEncoded)
if let strDecoded = strEncoded.removingPercentEncoding {
print(strDecoded)
}
}
I got annoyed with this problem so I took the answer from #sushain97 (thanks!) and built a deep encoder/decoder.
https://www.npmjs.com/package/firebase-key-encode
Basic usage:
var firebaseKeyEncode = require('firebase-key-encode');
firebaseKeyEncode.encode('my.bad.key');
// Output: my%2Ebad%2Ekey
Deep Usage:
var firebaseKeyEncode = require('firebase-key-encode');
var badTree = {
"pets": [
{
"jimmy.choo": 15}
],
"other.key": 5
}
firebaseKeyEncode.deepEncode(badTree);
// Output: {
// "pets": [
// {
// "jimmy%2Echoo": 15}
// ],
// "other%2Ekey": 5
// }
Personally, I found a simple and easy hack for this same problem I encountered
I took the dateTime string and convert it using replace('/','|')
the result will be something like this 2017|07|24 02:39:37 instead of 2017/07/24 02:39:37.
Even though it is not what OP asks,
but in my experience rather than using such dubious keys it is better to let .push() create an id,
and other things - e-mail, date etc. save as content of the dedicated fields.
$id: {
email: "Henry.Morgan#caribbean.sea"
}
P.S. Don't try to save volume by inserting what should be content into the key.
Premature optimization is the root of all evil (c).
Efficient C# implementation (for Unity and .net). Based on the answer from #josue.0.
public static string EncodeFirebaseKey(string s) {
StringBuilder sb = new StringBuilder();
foreach (char c in s) {
switch (c) {
case '_':
sb.Append("__");
break;
case '$':
sb.Append("_D");
break;
case '.':
sb.Append("_P");
break;
case '#':
sb.Append("_H");
break;
case '[':
sb.Append("_O");
break;
case ']':
sb.Append("_C");
break;
case '/':
sb.Append("_S");
break;
default:
sb.Append(c);
break;
}
}
return sb.ToString();
}
public static string DecodeFirebaseKey(string s) {
StringBuilder sb = new StringBuilder();
bool underscore = false;
for (int i = 0; i < s.Length; i++) {
if (underscore) {
switch (s[i]) {
case '_':
sb.Append('_');
break;
case 'D':
sb.Append('$');
break;
case 'P':
sb.Append('.');
break;
case 'H':
sb.Append('#');
break;
case 'O':
sb.Append('[');
break;
case 'C':
sb.Append(']');
break;
case 'S':
sb.Append('/');
break;
default:
Debug.LogWarning("Bad firebase key for decoding");
break;
}
underscore = false;
} else {
switch (s[i]) {
case '_':
underscore = true;
break;
default:
sb.Append(s[i]);
break;
}
}
}
return sb.ToString();
}
Python implementation
_escape = {'&': '&&',
'$': '&36',
'#': '&35',
'[': '&91',
']': '&93',
'/': '&47',
'.': '&46'}
_unescape = {e: u for u, e in _escape.items()}
def escape_firebase_key(text):
return text.translate(str.maketrans(_escape))
def unescape_firebase_key(text):
chunks = []
i = 0
while True:
a = text[i:].find('&')
if a == -1:
return ''.join(chunks + [text[i:]])
else:
if text[i+a:i+a+2] == '&&':
chunks.append('&')
i += a+2
else:
s = text[i+a:i+a+3]
if s in _unescape:
chunks.append(text[i:i+a])
chunks.append(_unescape[s])
i += a+3
else:
raise RuntimeError('Cannot unescape')
And a few test cases:
test_pairs = [('&hello.', '&&hello&46'),
('&&&', '&&&&&&'),
('some#email.com', 'some#email&46com'),
('#$[]/.', '&35&36&91&93&47&46')]
for u, e in test_pairs:
assert escape_firebase_key(u) == e, f"escaped '{u}' is '{e}', but was '{escape_firebase_key(u)}'"""
assert unescape_firebase_key(e) == u, f"unescaped '{e}' is '{u}', but was '{unescape_firebase_key(e)}'"
try:
unescape_firebase_key('&error')
assert False, 'Must have raised an exception here'
except RuntimeError as ex:
assert str(ex) == 'Cannot unescape'
const encodeKey = s => s.replace(/[\.\$\[\]#\/%]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase())
const decodeKey = s => s.replace(/%(2E|24|5B|5D|23|2F|25)/g, decodeURIComponent)
console.log(encodeKey('.$[]#/%23')) // %2E%24%5B%5D%23%2F%2523
console.log(decodeKey(encodeKey('.$[]#/%23'))) // .$[]#/%23
import re
import urllib.parse
encode_key = lambda s: re.sub('[\.\$\[\]#\/%]', lambda c: f'%{ord(c.group()):X}', s)
decode_key = lambda s: re.sub('%(2E|24|5B|5D|23|2F|25)', lambda c: urllib.parse.unquote(c.group()), s)
print(encode_key('.$[]#/%23')) # %2E%24%5B%5D%23%2F%2523
print(decode_key(encode_key('.$[]#/%23'))) # .$[]#/%23
Related
What is the preferred way to specify boolean value in the query part of URI? A normal query string looks like
a=foo&b=bar
Say I have a parameter "c" with boolean value, should I state
a=foo&b=bar&c=1
Or
a=foo&b=bar&c=True
Or
a=foo&b=bar&c=true
I checked the query component section of RFC 2396 and it does not specify how to express a boolean parameter. So what I want to know is what is the common (or reasonable) way to do it?
It completely depends on the way you read the query string. All of these that you ask for are valid.
Use key existence for boolean parameter like ?foo
For example, use ?foo instead of ?foo=true.
I prefer this way because I don't need to waste time to think or trace the source code about whether it should be true or 1 or enable or yes or anything that beyond my imagination.
In the case of case sensitivity, should it be true or True or TRUE?
In the case of term stemming, should it be enable or enabled?
IMHO, the form of ?foo is the most elegant way to pass a boolean variable to server because there are only 2 state of it (exist or not exist), which is good for representing a boolean variable.
This is also how Elasticsearch implemented for boolean query parameter, for example:
GET _cat/master?v
In node with an express server, you can add a boolean parser middleware like express-query-boolean.
var boolParser = require('express-query-boolean');
// [...]
app.use(bodyParser.json());
app.use(boolParser());
Without
// ?a=true&b[c]=false
console.log(req.query);
// => { a: 'true', b: { c: 'false' } }
With
// ?a=true&b[c]=false
console.log(req.query);
// => { a: true, b: { c: false } }
Url are strings and all values in a URL are strings,
all the params will be returned as strings.
it depends on how you interpret it in your code.
for the last one where c = true
you can do a JSON.parse(c)
which will change it to a boolean.
Also, you have to be careful not to pass it an empty string, if not it will throw an error.
I managed this with a custom function.
See browser compatibility here: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
function parseQuery(url = window.location.search) {
const urlParams = new URLSearchParams(url);
return Array.from(urlParams.keys()).reduce((acc, key) => {
const value = urlParams.get(key);
if (value) {
switch (value) {
case 'false': {
acc[key] = false;
break;
}
case 'true': {
acc[key] = true;
break;
}
case 'undefined': {
acc[key] = undefined;
break;
}
case 'null': {
acc[key] = null;
break;
}
default: {
acc[key] = value;
}
}
}
return acc;
}, {});
}
The function returns an object with all the parsed query parameters and fallback by default to the original value.
Or you can do this
const queryBool = unwrapTextBoolean({{{A_WAY_TO_GET_THAT_TEXT_BOOLEAN}}})
if(queryBool===null) {return;}
if(queryBool){
/*true-based code*/
}else{
/*false-based code*/
}
function unwrapTextBoolean(tB){
if(tB === 'true') return true;
if(tb === 'false') return false;
return null;
}
I have a series of "rows" in a collection which are persisted to a nosql database (in this case Firestore). Each one of my rows has a sort order which is established when the user adds, inserts, copies or moves rows with the collection. The insertion point into which a user may insert new records is arbitrary. The sort order is persisted to the backend, and can be retrieved ordered by the sort order field. There may be a large number of rows in the collection, on the order 50K.
The question what is the sort order encoding format that would permit repeated insertion of new records between existing records, without having to occasionally rewrite the sort index of the entire collection to provide "space" in the sort order between adjacent records.
I'm guessing there may some standard way to achieve this, but not sure what it is.
Assume the alphabet is "abc". Then:
b, c, cb...
is a lexicographically sorted list that allows you to insert items anywhere:
ab, b, bb, c, cab, cb, cbb...
And the result is still a list that allows you to insert items anywhere:
aab, ab, ac, b, bab, bb, bc, c, caab, cab, cac, cb, cbab, cbb, cbbb...
The trick is to avoid having "a" as the last character of an item, so that you can always put items behind others.
Do this with 64 ASCII characters instead of 3.
I've been thinking about this for quite a few months. This is my progress so far in implementing it. It still has some flaws and it's a little bit of a mess, but I guess I'll clean it and upload it at npm when I find more time.
// Originally written in TypeScript, then removed the types for SO.
const alphabet = 'abc';
function getHigherAsciiChar(char) {
const index = alphabet.indexOf(char);
if (index === alphabet.length - 1) {
return ''; // sorry, there's no higher character
}
const nextIndex = Math.ceil((index + alphabet.length - 1) / 2);
return alphabet.charAt(nextIndex);
}
function getCharBetween(minChar, maxChar) {
if (minChar > maxChar) {
throw new Error('minChar > maxChar, ' + minChar + ' > ' + maxChar);
}
const minIndex = alphabet.indexOf(minChar);
const maxIndex = alphabet.indexOf(maxChar);
const nextIndex = Math.floor((minIndex + maxIndex) / 2);
if (nextIndex === minIndex) {
return ''; // there is no character between these two
}
return alphabet.charAt(nextIndex);
}
function getPaddedString(finalLength, string) {
let result = string;
while (result.length < finalLength) {
result += alphabet.charAt(0);
}
return result;
}
function getOrderString(bounds) {
const console = { log: () => {} }; // uncomment this to log debug stuff
if (!bounds.previous && !bounds.next) {
return getHigherAsciiChar(alphabet[0]);
}
const previousString = bounds.previous || '';
if (!bounds.next) {
const firstPreviousChars = previousString.substr(0, previousString.length - 1);
const lastPreviousChar = previousString.charAt(previousString.length - 1);
return firstPreviousChars + (
getHigherAsciiChar(lastPreviousChar) || (
lastPreviousChar + getHigherAsciiChar(alphabet.charAt(0))
)
);
}
const nextString = bounds.next;
console.log(`Searching between '${previousString}' and '${nextString}'...`);
const bigStringLength = Math.max(previousString.length, nextString.length);
const previous = getPaddedString(bigStringLength, previousString);
const next = getPaddedString(bigStringLength, nextString);
console.log(previous, next);
let result = '';
let i;
for (i = 0; i < bigStringLength; i++) {
const previousChar = previous.charAt(i);
const nextChar = next.charAt(i);
// keep adding common characters
if (previousChar === nextChar) {
result += previousChar;
console.log(result, 'common character');
continue;
}
// when different characters are reached, try to add a character between these two
const charBetween = getCharBetween(previousChar, nextChar);
if (charBetween) {
result += charBetween;
console.log(result, 'character in-between. RETURNING');
// and you're done
return result;
}
// if there was no character between these two (their distance was exactly 1),
// repeat the low character, forget about the upper bound and just try to get bigger than lower bound
result += previousChar;
console.log(result, 'the lower character so we can forget about high bound');
i++;
break;
}
for (; previousString >= result; i++) {
const previousChar = previous.charAt(i);
const higherChar = getHigherAsciiChar(previousChar);
if (higherChar) {
// you found a digit that makes your result greater than the lower bound. You're done.
result += higherChar;
console.log(result, 'a higher character. RETURING');
return result;
}
// the digits are still very close, can't find a digit in-between (yet)
result += previousChar;
console.log(result, 'moving on to next digit');
}
// so you end up depleting all the character slots from the lower bound. Meh, just add any character.
result += getHigherAsciiChar(alphabet.charAt(0));
console.log(result, 'meh, just add any character. RETURNING');
return result;
}
function interleaveTest(order) {
const newOrder = [];
newOrder.push(getOrderString({ next: order[0] }));
for (let i = 0; i < order.length - 1; i++) {
newOrder.push(order[i]);
newOrder.push(getOrderString({ previous: order[i], next: order[i + 1] }));
}
newOrder.push(order[order.length - 1]);
newOrder.push(getOrderString({ previous: order[order.length - 1] }));
return newOrder;
}
let order = ['c'];
console.log('\n' + order.join(', ') + '\n');
order = interleaveTest(order);
console.log('\n' + order.join(', ') + '\n');
order = interleaveTest(order);
console.log('\n' + order.join(', ') + '\n');
order = interleaveTest(order);
console.log('\n' + order.join(', ') + '\n');
let atEnd = ['b'];
for (let i = 0; i < 10; i++) {
atEnd.push(getOrderString({ previous: atEnd[atEnd.length - 1] }));
}
console.log('\nat end: ' + atEnd.join(', ') + '\n');
I've written a algorithm to find parameters in a command line tool and are looking to clean up my code but is stuck.
The task
My program receives parameters as: flag output input ... | input flag output
Examples are: -d one/path second/path/to/file.txt and second/path/to/file.txt --dir one/path etc. Each space is used as a delimiter to create an array of parameters. A parameter can be either a flag such as -d or a path.
I got each flag mapped out in two arrays, which I zip into a an array of tuples. I call them the search set.
In math notation
I'm new to both FP and math notations so please forgive my mistakes (I've learned from wikipedia and other sites).
S for search and P for parameters
S = { S₁, S₂, S₃ }
where Sn = { flagShort, flagLong }
where flagShort = '-d' | '-r' | '-o'
flagLong = '--dir' | '--replace' | '--output'
P = { P₁, P₂, ... }
where Pn = flag | path
where path = dir | file
So I need to find the output by searching P for occurrences of Sn + the next parameter after the flag.
Ps = Sn ∩ P + Pₙ+₁
Input is just Ps ∉ P, so that is easy if I can get Ps.
Which leads me to the following transformation:
P -> Pn -> S -> Sn -> Sn = Pn -> Pn + Pₙ+₁
In javascript it can be written as:
const flagsShort = ["-r","-d","-o"]
const flagsLong = ["--replace","--dir","--output"]
const search = _.zip(flagsShort, flagsLong)
let Ps = tuplesIntersectionParametersPlusNextP(
['one/path', '--dir', 'two/path', '-d', 'one/path', '-d'],
search
)
// Ps -> [ '--dir', 'two/path', '-d', 'one/path' ]
function tuplesIntersectionParametersPlusNextP(P, S) {
const Ps = [];
P.forEach( (Pn, n) => {
S.forEach(Sn => {
Sn.forEach(flag => {
if(flag===Pn) Ps.push(Pn, P[n+1])
})
})
})
return Ps
}
While the code above works, it doesn't look clean. I've been looking around for different FP libraries such as underscore and different python articles but have yet to figure out how to use all these clever FP functions to clean up my code.
I will accept an answer in any language, Python, Haskell, Scala, etc but please don't use list comprehensions. While I'm very confident that I can port your code to js, I find list comprehensions a tad difficult to port. Better use map each reduce etc.
If you also can point me in the right direction with the set notations, I will be truly grateful!
Disclaimer
I tend to stay away from Javascript. There might be nicer ways to do certain things in Javascript, but I believe that the general principles still hold.
Your code is fine, albeit nested a little deep.
The trick in making your code cleaner is to extract abstract functionality. In the deepest nesting, what you're really asking is "does element Pn exist in the list of lists S?" This is something I can imagine myself asking more than once in an application, so it's perfect to turn into a function. You could even make this recursive for any level of nesting:
function ElementInNestedLists(e, l) {
if (l instanceof Array) {
return l.reduce(function(prev, cur, idx, arr) {
if (prev || ElementInNestedLists(e, cur)) {
return true;
}
return false;
}, false);
} else {
return l == e;
}
}
You don't have to use reduce here. There's nothing forbidding you from doing an actual for-loop in functional programming, in languages that support it. This does a better job at preventing the function from continuing after your element has been found:
function ElementInNestedLists(e, l) {
if (l instanceof Array) {
for (elem of l) {
if (ElementInNestedLists(e, elem)) {
return true;
}
}
return false;
} else {
return l == e;
}
}
Using this new function, you can simplify tuplesIntersectionParametersPlusNextP:
function tuplesIntersectionParametersPlusNextP(P, S) {
const Ps = [];
P.forEach( (Pn, n) => {
if (ElementInNestedLists(Pn, S)) {
Ps.push(Pn, P[n + 1]);
}
});
return Ps;
}
But there's a bug. The output of this function, given your input, is [ '--dir', 'two/path', '-d', 'one/path', _undefined_ ], because the last flag has no parameter following it. We need to add a test to ensure that there are at least two elements remaining to be checked.
function tuplesIntersectionParametersPlusNextP(P, S) {
const Ps = [];
P.forEach( (Pn, n) => {
if (n + 1 < P.length && ElementInNestedLists(Pn, S)) {
Ps.push(Pn, P[n + 1]);
}
});
return Ps;
}
But there's another bug. Consider the input ['-d', '-d', 'foo']. The output would be ['-d', '-d', '-d', 'foo'], which is incorrect. The desired output is ['-d', '-d']. You could decide to add a variable to track whether or not you need to process the next field:
function tuplesIntersectionParametersPlusNextP(P, S) {
const Ps = [];
skip = false;
P.forEach( (Pn, n) => {
if (skip) {
skip = false;
return;
}
if (n+1 < P.length && ElementInNestedLists(Pn, S)) {
Ps.push(Pn, P[n + 1]);
skip = true;
}
})
return Ps
}
And while this does what you want, you've now lost cleanliness again. The solution (as is often the case in functional programming) is to solve this problem recursively.
function Parse(cmd, flags, acc = []) {
if (cmd.length < 2) {
return acc;
}
if (ElementInNestedLists(cmd[0], flags)) {
acc.push(cmd[0], cmd[1]);
return Parse(cmd.slice(2, cmd.length), flags, acc)
}
return Parse(cmd.slice(1, cmd.length), flags, acc)
}
And if you don't want to slice:
function Parse(cmd, flags, idx = 0, acc = []) {
if (idx + 1 >= cmd.length) {
return acc;
}
if (ElementInNestedLists(cmd[idx], flags)) {
acc.push(cmd[idx], cmd[idx + 1]);
return Parse(cmd, flags, idx + 2, acc);
}
return Parse(cmd, flags, idx + 1, acc);
}
Of course, you might want to check and discard — or otherwise handle — a flag following a flag. There might be other, more complex requirements that you either haven't mentioned or haven't thought of (yet), but those are outside the scope of this answer.
int Emptylines(FILE *fp);
int Numberofstatements(FILE *fp);
int main() {
FILE *fp = NULL;
FILE *fp1 = NULL;
int n1, n2;
char fname[255], fname1[255];
printf("Enter file name for reading");
fflush(stdin);
scanf("%s", &fname);
fp = fopen(fname, "r");
if (fp == NULL) {
printf("File with name %s couldn't be open", fname);
exit(1);
}
n1 = Emptylines(fp); // this is for empty lines
n2 = Numberofstatements(fp);
printf("Insert file name for writing");
fflush(stdin);
scanf("%s", &fname1);
fp1 = fopen(fname1, "w+");
fprintf(fp1, "The number of empty lines=%d", n1);
fprintf(fp1, "The number of statements=%d", n2);
fclose(fp);
fclose(fp1);
return 0;
}
int Numberofstatements(FILE *fp) {
char line[128];
int nofstatements = 0;
while (fgets(line, sizeof line, fp) != NULL) {
if (strstr(line, "if") != 0)
nofstatements++;
}
return nofstatements;
}
I need to count all statements like if, do, while, break, etc. as well as empty lines and then save the result in a new file. I succeed in counting the empty lines but I have no idea how to count the statements. I tried 2 different ways but both failed.
I also need to ignore comments while reading the code, so if there is a for, while, etc. in the comments it shouldn't be counted.
A very basic answer addressing the fundamental issue (although there are others).
When you call int Numberofstatements(FILE *fp) you already reached the end of file in int Emptylines(FILE *fp); so you must add the statement
rewind(fp);
before trying to parse the file for a second time. Good luck with developing this.
OP asks: "Any ideas ?"
To do properly, suggest reading 1 char at a time. Keep track if you are in 1) on an include line, 2) inside a " " 3) inside a ' ' 4) in a // comment 5) inside a /* comment or 6) just plain code (watch for escape sequences). When in plain code look for the keywords do, while, etc. and all the while count the '\n'.
To do correctly - this is not an easy task - about 10x the code you have posted.
Sample beginning of a state machine.
state = plaincode;
while ((c = getc()) != EOF) {
switch (state) {
slashslash_commnet:
if (c == '\n) state = plaincode;
break;
plaincode:
if (c == '/') {
c2 = getc();
if (c2 == '/') { state = slashslash_commnet; break; }
else if (c2 == '*') { state = slashstar_commnet: break; }
else unget(c2);
else if (c == '\"') {
...
Is it possible to do a case sensitive find (search) in Dynamics AX 2009?
For example, when I am searching for "address", I don't want to see "Address" in the results.
Jan,
There IS a way to do it using standard Axapta X++. When you use the find screen there is a tab called 'Filter' where you can place code to do the filtering (no need to complete the fields on the name & location tab). The below code is for illustration purposes only as the below code is not complete and has not been finalised (I leave that to you).
str toMatch = 'Address';
str string;
str char, charMatch;
int i, pos;
boolean ret;
;
pos = strScan(_treeNodeName, toMatch, 1, strLen(_treeNodeName));
string = subStr(_treeNodeName, pos, strLen(toMatch));
if (string)
{
ret = true;
for (i=1;i<=strLen(toMatch);i++)
{
char = subStr(toMatch, i, 1);
charMatch = subStr(string, i, 1);
if (char2num(char,1) != char2num(charMatch,1))
{
ret = false;
}
}
if (ret)
{
return ret;
}
}
pos = strScan(_treeNodeSource, toMatch, 1, strLen(_treeNodeSource));
string = subStr(_treeNodeSource, pos, strLen(toMatch));
if (string)
{
ret = true;
for (i=1;i<=strLen(toMatch);i++)
{
char = subStr(toMatch, i, 1);
charMatch = subStr(string, i, 1);
if (char2num(char,1) != char2num(charMatch,1))
{
ret = false;
}
}
if (ret)
{
return ret;
}
}
return false;
If you have a look at the Find form window that appears when you do a find, look at the properties, this helps you narrow you down your search, unsure about a like-for-like exact match i.e. "address" and blocking out "Address".
No you cannot.
As mentioned in this answer, the find form uses the match method, which is documented on msdn here.
To quote MSDN;
Remarks
The system does not differentiate between lower and upper case.