PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/_octopress/source/functions/sscanf/index.markdown

https://gitlab.com/orvi2014/phpjs
Markdown | 281 lines | 259 code | 22 blank | 0 comment | 0 complexity | ee26fb20a5581fa519671184f8a5ea9e MD5 | raw file
  1. ---
  2. layout: page
  3. title: "JavaScript sscanf function"
  4. comments: true
  5. sharing: true
  6. footer: true
  7. alias:
  8. - /functions/view/sscanf:886
  9. - /functions/view/sscanf
  10. - /functions/view/886
  11. - /functions/sscanf:886
  12. - /functions/886
  13. ---
  14. <!-- Generated by Rakefile:build -->
  15. A JavaScript equivalent of PHP's sscanf
  16. {% codeblock strings/sscanf.js lang:js https://raw.github.com/kvz/phpjs/master/functions/strings/sscanf.js raw on github %}
  17. function sscanf(str, format) {
  18. // discuss at: http://phpjs.org/functions/sscanf/
  19. // original by: Brett Zamir (http://brett-zamir.me)
  20. // note: Since JS does not support scalar reference variables, any additional arguments to the function will
  21. // note: only be allowable here as strings referring to a global variable (which will then be set to the value
  22. // note: found in 'str' corresponding to the appropriate conversion specification in 'format'
  23. // note: I am unclear on how WS is to be handled here because documentation seems to me to contradict PHP behavior
  24. // example 1: sscanf('SN/2350001', 'SN/%d');
  25. // returns 1: [2350001]
  26. // example 2: var myVar; // Will be set by function
  27. // example 2: sscanf('SN/2350001', 'SN/%d', 'myVar');
  28. // returns 2: 1
  29. // example 3: sscanf("10--20", "%2$d--%1$d"); // Must escape '$' in PHP, but not JS
  30. // returns 3: [20, 10]
  31. // SETUP
  32. var retArr = [],
  33. num = 0,
  34. _NWS = /\S/,
  35. args = arguments,
  36. that = this,
  37. digit;
  38. var _setExtraConversionSpecs = function(offset) {
  39. // Since a mismatched character sets us off track from future legitimate finds, we just scan
  40. // to the end for any other conversion specifications (besides a percent literal), setting them to null
  41. // sscanf seems to disallow all conversion specification components (of sprintf) except for type specifiers
  42. //var matches = format.match(/%[+-]?([ 0]|'.)?-?\d*(\.\d+)?[bcdeufFosxX]/g); // Do not allow % in last char. class
  43. var matches = format.slice(offset)
  44. .match(/%[cdeEufgosxX]/g); // Do not allow % in last char. class;
  45. // b, F,G give errors in PHP, but 'g', though also disallowed, doesn't
  46. if (matches) {
  47. var lgth = matches.length;
  48. while (lgth--) {
  49. retArr.push(null);
  50. }
  51. }
  52. return _finish();
  53. };
  54. var _finish = function() {
  55. if (args.length === 2) {
  56. return retArr;
  57. }
  58. for (var i = 0; i < retArr.length; ++i) {
  59. that.window[args[i + 2]] = retArr[i];
  60. }
  61. return i;
  62. };
  63. var _addNext = function(j, regex, cb) {
  64. if (assign) {
  65. var remaining = str.slice(j);
  66. var check = width ? remaining.substr(0, width) : remaining;
  67. var match = regex.exec(check);
  68. var testNull = retArr[digit !== undefined ? digit : retArr.length] = match ? (cb ? cb.apply(null, match) :
  69. match[0]) : null;
  70. if (testNull === null) {
  71. throw 'No match in string';
  72. }
  73. return j + match[0].length;
  74. }
  75. return j;
  76. };
  77. if (arguments.length < 2) {
  78. throw 'Not enough arguments passed to sscanf';
  79. }
  80. // PROCESS
  81. for (var i = 0, j = 0; i < format.length; i++) {
  82. var width = 0,
  83. assign = true;
  84. if (format.charAt(i) === '%') {
  85. if (format.charAt(i + 1) === '%') {
  86. if (str.charAt(j) === '%') { // a matched percent literal
  87. ++i, ++j; // skip beyond duplicated percent
  88. continue;
  89. }
  90. // Format indicated a percent literal, but not actually present
  91. return _setExtraConversionSpecs(i + 2);
  92. }
  93. // CHARACTER FOLLOWING PERCENT IS NOT A PERCENT
  94. var prePattern = new RegExp('^(?:(\\d+)\\$)?(\\*)?(\\d*)([hlL]?)', 'g'); // We need 'g' set to get lastIndex
  95. var preConvs = prePattern.exec(format.slice(i + 1));
  96. var tmpDigit = digit;
  97. if (tmpDigit && preConvs[1] === undefined) {
  98. throw 'All groups in sscanf() must be expressed as numeric if any have already been used';
  99. }
  100. digit = preConvs[1] ? parseInt(preConvs[1], 10) - 1 : undefined;
  101. assign = !preConvs[2];
  102. width = parseInt(preConvs[3], 10);
  103. var sizeCode = preConvs[4];
  104. i += prePattern.lastIndex;
  105. // Fix: Does PHP do anything with these? Seems not to matter
  106. if (sizeCode) { // This would need to be processed later
  107. switch (sizeCode) {
  108. case 'h':
  109. // Treats subsequent as short int (for d,i,n) or unsigned short int (for o,u,x)
  110. case 'l':
  111. // Treats subsequent as long int (for d,i,n), or unsigned long int (for o,u,x);
  112. // or as double (for e,f,g) instead of float or wchar_t instead of char
  113. case 'L':
  114. // Treats subsequent as long double (for e,f,g)
  115. break;
  116. default:
  117. throw 'Unexpected size specifier in sscanf()!';
  118. break;
  119. }
  120. }
  121. // PROCESS CHARACTER
  122. try {
  123. switch (format.charAt(i + 1)) {
  124. // For detailed explanations, see http://web.archive.org/web/20031128125047/http://www.uwm.edu/cgi-bin/IMT/wwwman?topic=scanf%283%29&msection=
  125. // Also http://www.mathworks.com/access/helpdesk/help/techdoc/ref/sscanf.html
  126. // p, S, C arguments in C function not available
  127. // DOCUMENTED UNDER SSCANF
  128. case 'F':
  129. // Not supported in PHP sscanf; the argument is treated as a float, and
  130. // presented as a floating-point number (non-locale aware)
  131. // sscanf doesn't support locales, so no need for two (see %f)
  132. break;
  133. case 'g':
  134. // Not supported in PHP sscanf; shorter of %e and %f
  135. // Irrelevant to input conversion
  136. break;
  137. case 'G':
  138. // Not supported in PHP sscanf; shorter of %E and %f
  139. // Irrelevant to input conversion
  140. break;
  141. case 'b':
  142. // Not supported in PHP sscanf; the argument is treated as an integer, and presented as a binary number
  143. // Not supported - couldn't distinguish from other integers
  144. break;
  145. case 'i':
  146. // Integer with base detection (Equivalent of 'd', but base 0 instead of 10)
  147. j = _addNext(j, /([+-])?(?:(?:0x([\da-fA-F]+))|(?:0([0-7]+))|(\d+))/, function(num, sign, hex,
  148. oct, dec) {
  149. return hex ? parseInt(num, 16) : oct ? parseInt(num, 8) : parseInt(num, 10);
  150. });
  151. break;
  152. case 'n':
  153. // Number of characters processed so far
  154. retArr[digit !== undefined ? digit : retArr.length - 1] = j;
  155. break;
  156. // DOCUMENTED UNDER SPRINTF
  157. case 'c':
  158. // Get character; suppresses skipping over whitespace! (but shouldn't be whitespace in format anyways, so no difference here)
  159. // Non-greedy match
  160. j = _addNext(j, new RegExp('.{1,' + (width || 1) + '}'));
  161. break;
  162. case 'D':
  163. // sscanf documented decimal number; equivalent of 'd';
  164. case 'd':
  165. // Optionally signed decimal integer
  166. j = _addNext(j, /([+-])?(?:0*)(\d+)/, function(num, sign, dec) {
  167. // Ignores initial zeroes, unlike %i and parseInt()
  168. var decInt = parseInt((sign || '') + dec, 10);
  169. if (decInt < 0) { // PHP also won't allow less than -2147483648
  170. return decInt < -2147483648 ? -2147483648 : decInt; // integer overflow with negative
  171. } else { // PHP also won't allow greater than -2147483647
  172. return decInt < 2147483647 ? decInt : 2147483647;
  173. }
  174. });
  175. break;
  176. case 'f':
  177. // Although sscanf doesn't support locales, this is used instead of '%F'; seems to be same as %e
  178. case 'E':
  179. // These don't discriminate here as both allow exponential float of either case
  180. case 'e':
  181. j = _addNext(j, /([+-])?(?:0*)(\d*\.?\d*(?:[eE]?\d+)?)/, function(num, sign, dec) {
  182. if (dec === '.') {
  183. return null;
  184. }
  185. return parseFloat((sign || '') + dec); // Ignores initial zeroes, unlike %i and parseFloat()
  186. });
  187. break;
  188. case 'u':
  189. // unsigned decimal integer
  190. // We won't deal with integer overflows due to signs
  191. j = _addNext(j, /([+-])?(?:0*)(\d+)/, function(num, sign, dec) {
  192. // Ignores initial zeroes, unlike %i and parseInt()
  193. var decInt = parseInt(dec, 10);
  194. if (sign === '-') { // PHP also won't allow greater than 4294967295
  195. return 4294967296 - decInt; // integer overflow with negative
  196. } else {
  197. return decInt < 4294967295 ? decInt : 4294967295;
  198. }
  199. });
  200. break;
  201. case 'o':
  202. // Octal integer // Fix: add overflows as above?
  203. j = _addNext(j, /([+-])?(?:0([0-7]+))/, function(num, sign, oct) {
  204. return parseInt(num, 8);
  205. });
  206. break;
  207. case 's':
  208. // Greedy match
  209. j = _addNext(j, /\S+/);
  210. break;
  211. case 'X':
  212. // Same as 'x'?
  213. case 'x':
  214. // Fix: add overflows as above?
  215. // Initial 0x not necessary here
  216. j = _addNext(j, /([+-])?(?:(?:0x)?([\da-fA-F]+))/, function(num, sign, hex) {
  217. return parseInt(num, 16);
  218. });
  219. break;
  220. case '':
  221. // If no character left in expression
  222. throw 'Missing character after percent mark in sscanf() format argument';
  223. default:
  224. throw 'Unrecognized character after percent mark in sscanf() format argument';
  225. }
  226. } catch (e) {
  227. if (e === 'No match in string') { // Allow us to exit
  228. return _setExtraConversionSpecs(i + 2);
  229. }
  230. }++i; // Calculate skipping beyond initial percent too
  231. } else if (format.charAt(i) !== str.charAt(j)) {
  232. // Fix: Double-check i whitespace ignored in string and/or formats
  233. _NWS.lastIndex = 0;
  234. if ((_NWS)
  235. .test(str.charAt(j)) || str.charAt(j) === '') { // Whitespace doesn't need to be an exact match)
  236. return _setExtraConversionSpecs(i + 1);
  237. } else {
  238. // Adjust strings when encounter non-matching whitespace, so they align in future checks above
  239. str = str.slice(0, j) + str.slice(j + 1); // Ok to replace with j++;?
  240. i--;
  241. }
  242. } else {
  243. j++;
  244. }
  245. }
  246. // POST-PROCESSING
  247. return _finish();
  248. }
  249. {% endcodeblock %}
  250. - [Raw function on GitHub](https://github.com/kvz/phpjs/blob/master/functions/strings/sscanf.js)
  251. Please note that php.js uses JavaScript objects as substitutes for PHP arrays, they are
  252. the closest match to this hashtable-like data structure.
  253. Please also note that php.js offers community built functions and goes by the
  254. [McDonald's Theory](https://medium.com/what-i-learned-building/9216e1c9da7d). We'll put online
  255. functions that are far from perfect, in the hopes to spark better contributions.
  256. Do you have one? Then please just:
  257. - [Edit on GitHub](https://github.com/kvz/phpjs/edit/master/functions/strings/sscanf.js)
  258. ### Other PHP functions in the strings extension
  259. {% render_partial _includes/custom/strings.html %}