bool NumberValcon::parse(const QString& text) { if (text.isEmpty()) { _value = 0; return true; } UnicodeString utext = convertToICU(text); // Parse the number using ICU UErrorCode status = U_ZERO_ERROR; NumberFormat* fmt = NumberFormat::createInstance(status); if (U_SUCCESS(status)) { Formattable value; ParsePosition pos; fmt->parse(utext, value, pos); if (pos.getErrorIndex() == -1 && pos.getIndex() == utext.length()) { #if U_ICU_VERSION_MAJOR_NUM < 3 _value = value.getDouble(&status); #else _value = value.getDouble(status); #endif return true; } } return false; }
/** * Sample code for the C++ API to NumberFormat. */ void cppapi() { Locale us("en", "US"); UErrorCode status = U_ZERO_ERROR; // Create a number formatter for the US locale NumberFormat *fmt = NumberFormat::createInstance(us, status); check(status, "NumberFormat::createInstance"); // Parse a string. The string uses the digits '0' through '9' // and the decimal separator '.', standard in the US locale UnicodeString str("9876543210.123"); Formattable result; fmt->parse(str, result, status); check(status, "NumberFormat::parse"); printf("NumberFormat::parse(\""); // Display the result uprintf(str); printf("\") => "); uprintf(formattableToString(result)); printf("\n"); // Take the number parsed above, and use the formatter to // format it. str.remove(); // format() will APPEND to this string fmt->format(result, str, status); check(status, "NumberFormat::format"); printf("NumberFormat::format("); // Display the result uprintf(formattableToString(result)); printf(") => \""); uprintf(str); printf("\"\n"); delete fmt; // Release the storage used by the formatter }
/** * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of * NumberFormat. */ void IntlTestNumberFormatAPI::testAPI(/* char* par */) { UErrorCode status = U_ZERO_ERROR; // ======= Test constructors logln("Testing NumberFormat constructors"); NumberFormat *def = NumberFormat::createInstance(status); if(U_FAILURE(status)) { errln("ERROR: Could not create NumberFormat (default)"); } status = U_ZERO_ERROR; NumberFormat *fr = NumberFormat::createInstance(Locale::getFrench(), status); if(U_FAILURE(status)) { errln("ERROR: Could not create NumberFormat (French)"); } NumberFormat *cur = NumberFormat::createCurrencyInstance(status); if(U_FAILURE(status)) { errln("ERROR: Could not create NumberFormat (currency, default)"); } status = U_ZERO_ERROR; NumberFormat *cur_fr = NumberFormat::createCurrencyInstance(Locale::getFrench(), status); if(U_FAILURE(status)) { errln("ERROR: Could not create NumberFormat (currency, French)"); } NumberFormat *per = NumberFormat::createPercentInstance(status); if(U_FAILURE(status)) { errln("ERROR: Could not create NumberFormat (percent, default)"); } status = U_ZERO_ERROR; NumberFormat *per_fr = NumberFormat::createPercentInstance(Locale::getFrench(), status); if(U_FAILURE(status)) { errln("ERROR: Could not create NumberFormat (percent, French)"); } // ======= Test equality if (per_fr != NULL && cur_fr != NULL) { logln("Testing equality operator"); if( *per_fr == *cur_fr || ! ( *per_fr != *cur_fr) ) { errln("ERROR: == failed"); } } // ======= Test various format() methods if (cur_fr != NULL) { logln("Testing various format() methods"); double d = -10456.0037; int32_t l = 100000000; Formattable fD(d); Formattable fL(l); UnicodeString res1, res2, res3, res4, res5, res6; FieldPosition pos1(0), pos2(0), pos3(0), pos4(0); res1 = cur_fr->format(d, res1); logln( (UnicodeString) "" + (int32_t) d + " formatted to " + res1); res2 = cur_fr->format(l, res2); logln((UnicodeString) "" + (int32_t) l + " formatted to " + res2); res3 = cur_fr->format(d, res3, pos1); logln( (UnicodeString) "" + (int32_t) d + " formatted to " + res3); res4 = cur_fr->format(l, res4, pos2); logln((UnicodeString) "" + (int32_t) l + " formatted to " + res4); status = U_ZERO_ERROR; res5 = cur_fr->format(fD, res5, pos3, status); if(U_FAILURE(status)) { errln("ERROR: format(Formattable [double]) failed"); } logln((UnicodeString) "" + (int32_t) fD.getDouble() + " formatted to " + res5); status = U_ZERO_ERROR; res6 = cur_fr->format(fL, res6, pos4, status); if(U_FAILURE(status)) { errln("ERROR: format(Formattable [long]) failed"); } logln((UnicodeString) "" + fL.getLong() + " formatted to " + res6); } // ======= Test parse() if (fr != NULL) { logln("Testing parse()"); double d = -10456.0037; UnicodeString text("-10,456.0037"); Formattable result1, result2, result3; ParsePosition pos(0), pos01(0); fr->parseObject(text, result1, pos); if(result1.getType() != Formattable::kDouble && result1.getDouble() != d) { errln("ERROR: Roundtrip failed (via parse()) for " + text); } logln(text + " parsed into " + (int32_t) result1.getDouble()); fr->parse(text, result2, pos01); if(result2.getType() != Formattable::kDouble && result2.getDouble() != d) { errln("ERROR: Roundtrip failed (via parse()) for " + text); } logln(text + " parsed into " + (int32_t) result2.getDouble()); status = U_ZERO_ERROR; fr->parse(text, result3, status); if(U_FAILURE(status)) { errln("ERROR: parse() failed"); } if(result3.getType() != Formattable::kDouble && result3.getDouble() != d) { errln("ERROR: Roundtrip failed (via parse()) for " + text); } logln(text + " parsed into " + (int32_t) result3.getDouble()); } // ======= Test getters and setters if (fr != NULL && def != NULL) { logln("Testing getters and setters"); int32_t count = 0; const Locale *locales = NumberFormat::getAvailableLocales(count); logln((UnicodeString) "Got " + count + " locales" ); for(int32_t i = 0; i < count; i++) { UnicodeString name(locales[i].getName(),""); logln(name); } fr->setParseIntegerOnly( def->isParseIntegerOnly() ); if(fr->isParseIntegerOnly() != def->isParseIntegerOnly() ) { errln("ERROR: setParseIntegerOnly() failed"); } fr->setGroupingUsed( def->isGroupingUsed() ); if(fr->isGroupingUsed() != def->isGroupingUsed() ) { errln("ERROR: setGroupingUsed() failed"); } fr->setMaximumIntegerDigits( def->getMaximumIntegerDigits() ); if(fr->getMaximumIntegerDigits() != def->getMaximumIntegerDigits() ) { errln("ERROR: setMaximumIntegerDigits() failed"); } fr->setMinimumIntegerDigits( def->getMinimumIntegerDigits() ); if(fr->getMinimumIntegerDigits() != def->getMinimumIntegerDigits() ) { errln("ERROR: setMinimumIntegerDigits() failed"); } fr->setMaximumFractionDigits( def->getMaximumFractionDigits() ); if(fr->getMaximumFractionDigits() != def->getMaximumFractionDigits() ) { errln("ERROR: setMaximumFractionDigits() failed"); } fr->setMinimumFractionDigits( def->getMinimumFractionDigits() ); if(fr->getMinimumFractionDigits() != def->getMinimumFractionDigits() ) { errln("ERROR: setMinimumFractionDigits() failed"); } } // ======= Test getStaticClassID() logln("Testing getStaticClassID()"); status = U_ZERO_ERROR; NumberFormat *test = new DecimalFormat(status); if(U_FAILURE(status)) { errln("ERROR: Couldn't create a NumberFormat"); } if(test->getDynamicClassID() != DecimalFormat::getStaticClassID()) { errln("ERROR: getDynamicClassID() didn't return the expected value"); } delete test; delete def; delete fr; delete cur; delete cur_fr; delete per; delete per_fr; }
UBool TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign, int32_t& hour, int32_t& min, int32_t& sec) { static const int32_t kParseFailed = -99999; NumberFormat* numberFormat = 0; UnicodeString idUppercase = id; idUppercase.toUpper(); if (id.length() > GMT_ID_LENGTH && idUppercase.startsWith(GMT_ID)) { ParsePosition pos(GMT_ID_LENGTH); sign = 1; hour = 0; min = 0; sec = 0; if (id[pos.getIndex()] == MINUS /*'-'*/) { sign = -1; } else if (id[pos.getIndex()] != PLUS /*'+'*/) { return FALSE; } pos.setIndex(pos.getIndex() + 1); UErrorCode success = U_ZERO_ERROR; numberFormat = NumberFormat::createInstance(success); if(U_FAILURE(success)){ return FALSE; } numberFormat->setParseIntegerOnly(TRUE); // Look for either hh:mm, hhmm, or hh int32_t start = pos.getIndex(); Formattable n(kParseFailed); numberFormat->parse(id, n, pos); if (pos.getIndex() == start) { delete numberFormat; return FALSE; } hour = n.getLong(); if (pos.getIndex() < id.length()) { if (pos.getIndex() - start > 2 || id[pos.getIndex()] != COLON) { delete numberFormat; return FALSE; } // hh:mm pos.setIndex(pos.getIndex() + 1); int32_t oldPos = pos.getIndex(); n.setLong(kParseFailed); numberFormat->parse(id, n, pos); if ((pos.getIndex() - oldPos) != 2) { // must be 2 digits delete numberFormat; return FALSE; } min = n.getLong(); if (pos.getIndex() < id.length()) { if (id[pos.getIndex()] != COLON) { delete numberFormat; return FALSE; } // [:ss] pos.setIndex(pos.getIndex() + 1); oldPos = pos.getIndex(); n.setLong(kParseFailed); numberFormat->parse(id, n, pos); if (pos.getIndex() != id.length() || (pos.getIndex() - oldPos) != 2) { delete numberFormat; return FALSE; } sec = n.getLong(); } } else { // Supported formats are below - // // HHmmss // Hmmss // HHmm // Hmm // HH // H int32_t length = pos.getIndex() - start; if (length <= 0 || 6 < length) { // invalid length delete numberFormat; return FALSE; } switch (length) { case 1: case 2: // already set to hour break; case 3: case 4: min = hour % 100; hour /= 100; break; case 5: case 6: sec = hour % 100; min = (hour/100) % 100; hour /= 10000; break; } } delete numberFormat; if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) { return FALSE; } return TRUE; } return FALSE; }
// test RBNF extensions to message format void TestMessageFormat::TestRBNF(void) { // WARNING: this depends on the RBNF formats for en_US Locale locale("en", "US", ""); UErrorCode ec = U_ZERO_ERROR; UnicodeString values[] = { // decimal values do not format completely for ordinal or duration, and // do not always parse, so do not include them "0", "1", "12", "100", "123", "1001", "123,456", "-17", }; int32_t values_count = sizeof(values)/sizeof(values[0]); UnicodeString formats[] = { "There are {0,spellout} files to search.", "There are {0,spellout,%simplified} files to search.", "The bogus spellout {0,spellout,%BOGUS} files behaves like the default.", "This is the {0,ordinal} file to search.", // TODO fix bug, ordinal does not parse "Searching this file will take {0,duration} to complete.", "Searching this file will take {0,duration,%with-words} to complete.", }; int32_t formats_count = sizeof(formats)/sizeof(formats[0]); Formattable args[1]; NumberFormat* numFmt = NumberFormat::createInstance(locale, ec); if (U_FAILURE(ec)) { dataerrln("Error calling NumberFormat::createInstance()"); return; } for (int i = 0; i < formats_count; ++i) { MessageFormat* fmt = new MessageFormat(formats[i], locale, ec); logln((UnicodeString)"Testing format pattern: '" + formats[i] + "'"); for (int j = 0; j < values_count; ++j) { ec = U_ZERO_ERROR; numFmt->parse(values[j], args[0], ec); if (U_FAILURE(ec)) { errln((UnicodeString)"Failed to parse test argument " + values[j]); } else { FieldPosition fp(0); UnicodeString result; fmt->format(args, 1, result, fp, ec); logln((UnicodeString)"value: " + toString(args[0]) + " --> " + result + UnicodeString(" ec: ") + u_errorName(ec)); if (i != 3) { // TODO: fix this, for now skip ordinal parsing (format string at index 3) int32_t count = 0; Formattable* parseResult = fmt->parse(result, count, ec); if (count != 1) { errln((UnicodeString)"parse returned " + count + " args"); } else if (parseResult[0] != args[0]) { errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0])); } delete []parseResult; } } } delete fmt; } delete numFmt; }
/** * Parses a string using the rule set or DecimalFormat belonging * to this substitution. If there's a match, a mathematical * operation (the inverse of the one used in formatting) is * performed on the result of the parse and the value passed in * and returned as the result. The parse position is updated to * point to the first unmatched character in the string. * @param text The string to parse * @param parsePosition On entry, ignored, but assumed to be 0. * On exit, this is updated to point to the first unmatched * character (or 0 if the substitution didn't match) * @param baseValue A partial parse result that should be * combined with the result of this parse * @param upperBound When searching the rule set for a rule * matching the string passed in, only rules with base values * lower than this are considered * @param lenientParse If true and matching against rules fails, * the substitution will also try matching the text against * numerals using a default-costructed NumberFormat. If false, * no extra work is done. (This value is false whenever the * formatter isn't in lenient-parse mode, but is also false * under some conditions even when the formatter _is_ in * lenient-parse mode.) * @return If there's a match, this is the result of composing * baseValue with whatever was returned from matching the * characters. This will be either a Long or a Double. If there's * no match this is new Long(0) (not null), and parsePosition * is left unchanged. */ UBool NFSubstitution::doParse(const UnicodeString& text, ParsePosition& parsePosition, double baseValue, double upperBound, UBool lenientParse, Formattable& result) const { #ifdef RBNF_DEBUG fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound); #endif // figure out the highest base value a rule can have and match // the text being parsed (this varies according to the type of // substitutions: multiplier, modulus, and numerator substitutions // restrict the search to rules with base values lower than their // own; same-value substitutions leave the upper bound wherever // it was, and the others allow any rule to match upperBound = calcUpperBound(upperBound); // use our rule set to parse the text. If that fails and // lenient parsing is enabled (this is always false if the // formatter's lenient-parsing mode is off, but it may also // be false even when the formatter's lenient-parse mode is // on), then also try parsing the text using a default- // constructed NumberFormat if (ruleSet != NULL) { ruleSet->parse(text, parsePosition, upperBound, result); if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { UErrorCode status = U_ZERO_ERROR; NumberFormat* fmt = NumberFormat::createInstance(status); if (U_SUCCESS(status)) { fmt->parse(text, result, parsePosition); } delete fmt; } // ...or use our DecimalFormat to parse the text } else if (numberFormat != NULL) { numberFormat->parse(text, result, parsePosition); } // if the parse was successful, we've already advanced the caller's // parse position (this is the one function that doesn't have one // of its own). Derive a parse result and return it as a Long, // if possible, or a Double if (parsePosition.getIndex() != 0) { UErrorCode status = U_ZERO_ERROR; double tempResult = result.getDouble(status); // composeRuleValue() produces a full parse result from // the partial parse result passed to this function from // the caller (this is either the owning rule's base value // or the partial result obtained from composing the // owning rule's base value with its other substitution's // parse result) and the partial parse result obtained by // matching the substitution (which will be the same value // the caller would get by parsing just this part of the // text with RuleBasedNumberFormat.parse() ). How the two // values are used to derive the full parse result depends // on the types of substitutions: For a regular rule, the // ultimate result is its multiplier substitution's result // times the rule's divisor (or the rule's base value) plus // the modulus substitution's result (which will actually // supersede part of the rule's base value). For a negative- // number rule, the result is the negative of its substitution's // result. For a fraction rule, it's the sum of its two // substitution results. For a rule in a fraction rule set, // it's the numerator substitution's result divided by // the rule's base value. Results from same-value substitutions // propagate back upard, and null substitutions don't affect // the result. tempResult = composeRuleValue(tempResult, baseValue); result.setDouble(tempResult); return TRUE; // if the parse was UNsuccessful, return 0 } else { result.setLong(0); return FALSE; } }
UBool FractionalPartSubstitution::doParse(const UnicodeString& text, ParsePosition& parsePosition, double baseValue, double /*upperBound*/, UBool lenientParse, Formattable& resVal) const { // if we're not in byDigits mode, we can just use the inherited // doParse() if (!byDigits) { return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); // if we ARE in byDigits mode, parse the text one digit at a time // using this substitution's owning rule set (we do this by setting // upperBound to 10 when calling doParse() ) until we reach // nonmatching text } else { UnicodeString workText(text); ParsePosition workPos(1); double result = 0; int32_t digit; // double p10 = 0.1; DigitList dl; NumberFormat* fmt = NULL; while (workText.length() > 0 && workPos.getIndex() != 0) { workPos.setIndex(0); Formattable temp; getRuleSet()->parse(workText, workPos, 10, temp); UErrorCode status = U_ZERO_ERROR; digit = temp.getLong(status); // digit = temp.getType() == Formattable::kLong ? // temp.getLong() : // (int32_t)temp.getDouble(); if (lenientParse && workPos.getIndex() == 0) { if (!fmt) { status = U_ZERO_ERROR; fmt = NumberFormat::createInstance(status); if (U_FAILURE(status)) { delete fmt; fmt = NULL; } } if (fmt) { fmt->parse(workText, temp, workPos); digit = temp.getLong(status); } } if (workPos.getIndex() != 0) { dl.append((char)('0' + digit)); // result += digit * p10; // p10 /= 10; parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); workText.removeBetween(0, workPos.getIndex()); while (workText.length() > 0 && workText.charAt(0) == gSpace) { workText.removeBetween(0, 1); parsePosition.setIndex(parsePosition.getIndex() + 1); } } } delete fmt; result = dl.fCount == 0 ? 0 : dl.getDouble(); result = composeRuleValue(result, baseValue); resVal.setDouble(result); return TRUE; } }
std::string GlobalizationNDK::stringToNumber(const std::string& args) { if (args.empty()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: no arguments provided!"); return errorInJson(PARSING_ERROR, "No arguments provided!"); } Json::Reader reader; Json::Value root; bool parse = reader.parse(args, root); if (!parse) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: invalid json data: %s", args.c_str()); return errorInJson(PARSING_ERROR, "Invalid json data!"); } Json::Value sv = root["numberString"]; if (sv.isNull()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: no numberString provided!"); return errorInJson(FORMATTING_ERROR, "No numberString provided!"); } if (!sv.isString()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: invalid numberString type: %d!", sv.type()); return errorInJson(FORMATTING_ERROR, "Invalid numberString type!"); } std::string str = sv.asString(); if (str.empty()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: empty numberString!"); return errorInJson(FORMATTING_ERROR, "Empty numberString!"); } // This is the default value when no options provided. ENumberType type = kNumberDecimal; Json::Value options = root["options"]; std::string error; if (!handleNumberOptions(options, type, error)) return errorInJson(PARSING_ERROR, error); UErrorCode status = U_ZERO_ERROR; NumberFormat* nf; switch (type) { case kNumberDecimal: default: nf = NumberFormat::createInstance(status); break; case kNumberCurrency: nf = NumberFormat::createCurrencyInstance(status); break; case kNumberPercent: nf = NumberFormat::createPercentInstance(status); break; } if (!nf) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: failed to create NumberFormat instance for type %d: %d", status, type); return errorInJson(UNKNOWN_ERROR, "Failed to create NumberFormat instance!"); } std::auto_ptr<NumberFormat> deleter(nf); UnicodeString uStr = UnicodeString::fromUTF8(str); Formattable value; if (type == kNumberCurrency) { ParsePosition pos; CurrencyAmount* ca = nf->parseCurrency(uStr, pos); if (ca) value = ca->getNumber(); else nf->parse(uStr, value, status); } else nf->parse(uStr, value, status); if (status != U_ZERO_ERROR && status != U_ERROR_WARNING_START) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: failed (%d) to parse string: %s", status, str.c_str()); return errorInJson(PARSING_ERROR, "Failed to parse string!"); } if (!value.isNumeric()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: string is not numeric: %s", str.c_str()); return errorInJson(FORMATTING_ERROR, "String is not numeric!"); } return resultInJson(value.getDouble()); }
/** * Parse a custom time zone identifier and return a corresponding zone. * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or * GMT[+-]hh. * @return a newly created SimpleTimeZone with the given offset and * no Daylight Savings Time, or null if the id cannot be parsed. */ TimeZone* TimeZone::createCustomTimeZone(const UnicodeString& id) { static const int32_t kParseFailed = -99999; NumberFormat* numberFormat = 0; UnicodeString idUppercase = id; idUppercase.toUpper(); if (id.length() > GMT_ID_LENGTH && idUppercase.startsWith(GMT_ID)) { ParsePosition pos(GMT_ID_LENGTH); UBool negative = FALSE; int32_t offset; if (id[pos.getIndex()] == 0x002D /*'-'*/) negative = TRUE; else if (id[pos.getIndex()] != 0x002B /*'+'*/) return 0; pos.setIndex(pos.getIndex() + 1); UErrorCode success = U_ZERO_ERROR; numberFormat = NumberFormat::createInstance(success); numberFormat->setParseIntegerOnly(TRUE); // Look for either hh:mm, hhmm, or hh int32_t start = pos.getIndex(); Formattable n(kParseFailed); numberFormat->parse(id, n, pos); if (pos.getIndex() == start) { delete numberFormat; return 0; } offset = n.getLong(); if (pos.getIndex() < id.length() && id[pos.getIndex()] == 0x003A /*':'*/) { // hh:mm offset *= 60; pos.setIndex(pos.getIndex() + 1); int32_t oldPos = pos.getIndex(); n.setLong(kParseFailed); numberFormat->parse(id, n, pos); if (pos.getIndex() == oldPos) { delete numberFormat; return 0; } offset += n.getLong(); } else { // hhmm or hh // Be strict about interpreting something as hh; it must be // an offset < 30, and it must be one or two digits. Thus // 0010 is interpreted as 00:10, but 10 is interpreted as // 10:00. if (offset < 30 && (pos.getIndex() - start) <= 2) offset *= 60; // hh, from 00 to 29; 30 is 00:30 else offset = offset % 100 + offset / 100 * 60; // hhmm } if(negative) offset = -offset; delete numberFormat; return new SimpleTimeZone(offset * 60000, CUSTOM_ID); } return 0; }