Jak mogę przeanalizować ciąg z przecinkiem separatora tysięcy na liczbę?


104

Mam 2,299.00jako ciąg i próbuję przeanalizować go na liczbę. Próbowałem użyć parseFloat, co daje 2. Myślę, że problem stanowi przecinek, ale jak mam rozwiązać ten problem we właściwy sposób? Po prostu usunąć przecinek?

var x = parseFloat("2,299.00")
alert(x);

Odpowiedzi:


121

Tak, usuń przecinki:

parseFloat(yournumber.replace(/,/g, ''));

7
Tak, ale teraz miejsca po przecinku są stracone. Na przykład 2300,00 daje 2300.
— user1540714

@ user1540714 to dlatego, że jest to liczba zmiennoprzecinkowa, a nie ciąg znaków. Jeśli chcesz go następnie wyprowadzić, musisz go sformatować, aby zawsze pokazywał 2 miejsca dziesiętne.
— Jon Taylor

Czy można tego uniknąć? Spróbuję naprawić
— user1540714

38
Mogę zasugerować, abyśmy zamiast tego przechwycili wszystkie przecinki za pomocą: .replace(/,/g, '') To wyrażenie regularne używa globalnego gdo zastąpienia wszystkiego.
— gdibble

32
Nie mam pojęcia, dlaczego ta odpowiedź otrzymała tak wiele głosów za i została wybrana jako poprawna, ma dwa poważne problemy. 1. Nie radzi sobie z liczbami z wieloma przecinkami, np. parseFloat("2,000,000.00".replace(',',''))Zwraca 2000i 2. zawodzi w wielu regionach świata, w których ,występuje miejsce po przecinku, np. parseFloat("2.000.000,00".replace(',',''))Zwraca 2- zobacz moją odpowiedź poniżej na coś, co działa na całym świecie.
— David Meister

113

Usuwanie przecinków jest potencjalnie niebezpieczne, ponieważ, jak wspominali inni w komentarzach, w wielu lokalizacjach przecinek oznacza coś innego (np. Miejsce dziesiętne).

Nie wiem, skąd masz swój ciąg, ale w niektórych miejscach na świecie "2,299.00"=2.299

IntlPrzedmiot mógł być dobry sposób, aby rozwiązać ten problem, ale jakoś udało im się wysyłać spec tylko z Intl.NumberFormat.format()API i bez parseodpowiednika :(

Jedynym sposobem na przeanalizowanie ciągu znaków z kulturowymi znakami numerycznymi do postaci rozpoznawalnej przez maszynę liczby w jakikolwiek rozsądny sposób i18n jest użycie biblioteki, która wykorzystuje dane CLDR do pokrycia wszystkich możliwych sposobów formatowania ciągów liczbowych http: //cldr.unicode. org /

Dwie najlepsze opcje JS, z jakimi się do tej pory spotkałem:


4
Nie mogę uwierzyć, że nikt jeszcze nie zagłosował za tą odpowiedzią, to jedyna rzeczywista odpowiedź na tej stronie!
— evilkos

9
Całkowicie się zgadzam, że Intl powinien mieć odpowiednik parsujący. Wydaje się oczywiste, że ludzie będą tego potrzebować.
— karola,

To jedyny systematyczny sposób, aby to zrobić. Nie można tego osiągnąć za pomocą jednego wyrażenia regularnego, które pasuje do wszystkich. Przecinki i kropki mają różne znaczenie w różnych językach.
— Wildhammer

38

W nowoczesnych przeglądarkach możesz użyć wbudowanego Intl.NumberFormat, aby wykryć formatowanie liczb w przeglądarce i znormalizować dane wejściowe, aby pasowały.

function parseNumber(value, locale = navigator.language) {
  const example = Intl.NumberFormat(locale).format('1.1');
  const cleanPattern = new RegExp(`[^-+0-9${ example.charAt( 1 ) }]`, 'g');
  const cleaned = value.replace(cleanPattern, '');
  const normalized = cleaned.replace(example.charAt(1), '.');

  return parseFloat(normalized);
}

const corpus = {
  '1.123': {
    expected: 1.123,
    locale: 'en-US'
  },
  '1,123': {
    expected: 1123,
    locale: 'en-US'
  },
  '2.123': {
    expected: 2123,
    locale: 'fr-FR'
  },
  '2,123': {
    expected: 2.123,
    locale: 'fr-FR'
  },
}


for (const candidate in corpus) {
  const {
    locale,
    expected
  } = corpus[candidate];
  const parsed = parseNumber(candidate, locale);

  console.log(`${ candidate } in ${ corpus[ candidate ].locale } == ${ expected }? ${ parsed === expected }`);
}

Jest oczywiście miejsce na optymalizację i buforowanie, ale działa to niezawodnie we wszystkich językach.


Dlaczego to nie ma wielu pozytywnych głosów !!! To najbardziej eleganckie rozwiązanie INPUT w formatach międzynarodowych! Dziękuję Ci!!
— kpollock

we Francji walutą jest 231 123
— 413,12

26

Usuń wszystko, co nie jest cyfrą, kropką dziesiętną ani znakiem minus ( -):

var str = "2,299.00";
str = str.replace(/[^\d\.\-]/g, ""); // You might also include + if you want them to be able to type it
var num = parseFloat(str);

Zaktualizowane skrzypce

Zauważ, że nie będzie działać dla liczb w notacji naukowej. Jeśli chcesz go zmienić replacelinię dodać e, Ei +do listy dopuszczalnych znaków:

str = str.replace(/[^\d\.\-eE+]/g, "");

4
To nie zadziała w przypadku liczb ujemnych lub liczb w notacji naukowej.
— Aadit M Shah

1
str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1"); To jest to, czego bym użył, ale jestem całkowicie bezużyteczny w regex i jest to coś, co znalazłem jakiś czas temu w innym wątku SO.
— Jon Taylor

@JonTaylor: Celem tutaj nie było zweryfikowanie numeru, tylko sprawienie, by działał parseFloat- co to zweryfikuje. :-)
— TJ Crowder

co moje robi, o ile wiem. Używam tego do konwersji 1,234,567 itd. Na 1234567. Jak już powiedziałem, jestem całkowicie bezużyteczny w regex, więc za całe życie nie mogłem ci powiedzieć, co tak naprawdę robi lol.
— Jon Taylor

1
zły pomysł, aby usunąć znak minus "-" - uzyskaj zupełnie inną liczbę, a także jeśli przeanalizujesz formaty heterogeniczne, "7.500"! == "7500"
— Serge

14

Zwykle należy rozważyć użycie pól wejściowych, które nie pozwalają na dowolne wprowadzanie tekstu dla wartości liczbowych. Ale mogą być przypadki, kiedy trzeba odgadnąć format wejściowy. Na przykład 1,234,56 w Niemczech oznacza 1234,56 w USA. Wejdź na https://salesforce.stackexchange.com/a/21404, aby zapoznać się z listą krajów, w których przecinek jest dziesiętny.

Używam następującej funkcji, aby zgadnąć i usunąć wszystkie znaki nienumeryczne:

function parseNumber(strg) {
    var strg = strg || "";
    var decimal = '.';
    strg = strg.replace(/[^0-9$.,]/g, '');
    if(strg.indexOf(',') > strg.indexOf('.')) decimal = ',';
    if((strg.match(new RegExp("\\" + decimal,"g")) || []).length > 1) decimal="";
    if (decimal != "" && (strg.length - strg.indexOf(decimal) - 1 == 3) && strg.indexOf("0" + decimal)!==0) decimal = "";
    strg = strg.replace(new RegExp("[^0-9$" + decimal + "]","g"), "");
    strg = strg.replace(',', '.');
    return parseFloat(strg);
}   

Wypróbuj tutaj: https://plnkr.co/edit/9p5Y6H?p=preview

Przykłady:

1.234,56 € => 1234.56
1,234.56USD => 1234.56
1,234,567€ => 1234567
1.234.567 => 1234567
1,234.567 => 1234.567
1.234 => 1234 // might be wrong - best guess
1,234 => 1234 // might be wrong - best guess
1.2345 => 1.2345
0,123 => 0.123

Funkcja ma jeden słaby punkt: nie można odgadnąć formatu, jeśli masz 1,123 lub 1,123 - ponieważ w zależności od formatu ustawień regionalnych oba mogą być przecinkiem lub separatorem tysięcy. W tym szczególnym przypadku funkcja potraktuje separator jako separator tysięcy i zwróci 1123.


Nie udaje się w przypadku liczb takich jak 1111,11, co jest oczywiście formatem angielskim, ale zwraca 111111
— Mr. Goferito

Dziękuję panie Goferito - przepraszam - naprawiłem funkcję.
— Gerfried

Wygląda na to, że na przykład może to się nie powieść w przypadku bardzo małych liczb „0,124” w języku francuskim.
— Paul Alexander

Świetnie! To jest prawie dokładnie to, czego szukałem: 3,00 €również jest zamienione na 3,00. Tylko uwaga, która 3,001jest sformatowana na 3001. Aby tego uniknąć, dane wejściowe powinny zawsze zawierać symbole dziesiętne. Np. 3,001.00€ 3,001.00Konwertuje poprawnie. Zaktualizuj jsfiddle. 0,124nadal jest konwertowany na 124
— Arnis Juraga

dobra robota kolego Myślę, że warto podać poprawną odpowiedź
— Waheed

5

Zaskakujące jest to, że uwzględnili toLocaleString, ale nie metodę analizy . Przynajmniej toLocaleString bez argumentów jest dobrze obsługiwana w IE6 +.

W przypadku rozwiązania i18n wymyśliłem to:

Najpierw wykryj lokalny separator dziesiętny użytkownika:

var decimalSeparator = 1.1;
decimalSeparator = decimalSeparator.toLocaleString().substring(1, 2);

Następnie znormalizuj liczbę, jeśli w ciągu znaków jest więcej niż jeden separator dziesiętny:

var pattern = "([" + decimalSeparator + "])(?=.*\\1)";separator
var formatted = valor.replace(new RegExp(pattern, "g"), "");

Na koniec usuń wszystko, co nie jest liczbą ani separatorem dziesiętnym:

formatted = formatted.replace(new RegExp("[^0-9" + decimalSeparator + "]", "g"), '');
return Number(formatted.replace(decimalSeparator, "."));

5

Jest to uproszczone, dyskretne opakowanie wokół parseFloatfunkcji.

function parseLocaleNumber(str) {
  // Detect the user's locale decimal separator:
  var decimalSeparator = (1.1).toLocaleString().substring(1, 2);
  // Detect the user's locale thousand separator:
  var thousandSeparator = (1000).toLocaleString().substring(1, 2);
  // In case there are locales that don't use a thousand separator
  if (thousandSeparator.match(/\d/))
    thousandSeparator = '';

  str = str
    .replace(new RegExp(thousandSeparator, 'g'), '')
    .replace(new RegExp(decimalSeparator), '.')

  return parseFloat(str);
}

2

Jeśli chcesz uniknąć problemu, który opublikował David Meister i masz pewność co do liczby miejsc po przecinku, możesz zamienić wszystkie kropki i przecinki i podzielić przez 100, np .:

var value = "2,299.00";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/100;

lub jeśli masz 3 miejsca po przecinku

var value = "2,299.001";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/1000;

To zależy od Ciebie, czy chcesz używać parseInt, parseFloat czy Number. Jeśli chcesz zachować liczbę miejsc dziesiętnych, możesz użyć funkcji .toFixed (...).


1

Wszystkie te odpowiedzi zawodzą, jeśli masz liczbę w milionach.

3,456,789 zwróciłoby po prostu 3456 przy użyciu metody replace.

Najbardziej poprawną odpowiedzią na proste usunięcie przecinków powinno być.

var number = '3,456,789.12';
number.split(',').join('');
/* number now equips 3456789.12 */
parseFloat(number);

Lub po prostu napisane.

number = parseFloat(number.split(',').join(''));

Oczywiście amerykańskie używają przecinka, a nie kropki. Byłoby głupotą próbować zrobić potworność, aby poradzić sobie z jednym i drugim.
— Sprawa

2
ale taka „potworność” już istnieje jako część tego, co zapewnia Unicode (zobacz moją odpowiedź). Jestem pewien, że czułbyś się mniej głupio, gdybyś prowadził firmę z międzynarodowymi klientami.
— David Meister

1

To konwertuje liczbę w dowolnym miejscu na normalną liczbę. Działa również w przypadku miejsc dziesiętnych:

function numberFromLocaleString(stringValue, locale){
    var parts = Number(1111.11).toLocaleString(locale).replace(/\d+/g,'').split('');
    if (stringValue === null)
        return null;
    if (parts.length==1) {
        parts.unshift('');
    }   
    return Number(String(stringValue).replace(new RegExp(parts[0].replace(/\s/g,' '),'g'), '').replace(parts[1],"."));
}
//Use default browser locale
numberFromLocaleString("1,223,333.567") //1223333.567

//Use specific locale
numberFromLocaleString("1 223 333,567", "ru") //1223333.567

1

Dzięki tej funkcji będziesz mógł formatować wartości w wielu formatach, takich jak 1.234,56i 1,234.56, a nawet z błędami, takimi jak 1.234.56i1,234,56

/**
 * @param {string} value: value to convert
 * @param {bool} coerce: force float return or NaN
 */
function parseFloatFromString(value, coerce) {
    value = String(value).trim();

    if ('' === value) {
        return value;
    }

    // check if the string can be converted to float as-is
    var parsed = parseFloat(value);
    if (String(parsed) === value) {
        return fixDecimals(parsed, 2);
    }

    // replace arabic numbers by latin
    value = value
    // arabic
    .replace(/[\u0660-\u0669]/g, function(d) {
        return d.charCodeAt(0) - 1632;
    })

    // persian
    .replace(/[\u06F0-\u06F9]/g, function(d) {
        return d.charCodeAt(0) - 1776;
    });

    // remove all non-digit characters
    var split = value.split(/[^\dE-]+/);

    if (1 === split.length) {
        // there's no decimal part
        return fixDecimals(parseFloat(value), 2);
    }

    for (var i = 0; i < split.length; i++) {
        if ('' === split[i]) {
            return coerce ? fixDecimals(parseFloat(0), 2) : NaN;
        }
    }

    // use the last part as decimal
    var decimal = split.pop();

    // reconstruct the number using dot as decimal separator
    return fixDecimals(parseFloat(split.join('') +  '.' + decimal), 2);
}

function fixDecimals(num, precision) {
    return (Math.floor(num * 100) / 100).toFixed(precision);
}
parseFloatFromString('1.234,56')
"1234.56"
parseFloatFromString('1,234.56')
"1234.56"
parseFloatFromString('1.234.56')
"1234.56"
parseFloatFromString('1,234,56')
"1234.56"

0

Jeśli chcesz uzyskać odpowiedź l10n, zrób to w ten sposób. Przykład używa waluty, ale nie potrzebujesz tego. Biblioteka Intl będzie musiała zostać wypełniona, jeśli musisz obsługiwać starsze przeglądarki.

var value = "2,299.00";
var currencyId = "USD";
var nf = new Intl.NumberFormat(undefined, {style:'currency', currency: currencyId, minimumFractionDigits: 2});

value = nf.format(value.replace(/,/g, ""));

9
to nie jest tak naprawdę odpowiedź l10n, ponieważ dla niektórych ustawień regionalnych (np. DE) przecinek jest kropką dziesiętną.
— Andreas

A także replace()zastępuje pierwsze dopasowanie tylko wtedy, gdy nie jest używane RegExpz 'g'flagą.
— binki

@binki Thanks. Naprawiony.
— Tony Topper,

@Andreas To jest odpowiedź l10n. Jeśli chcesz mieć inną walutę, po prostu zmienisz wartość currencyId na ten kraj currencyId.
— Tony Topper,

1
@TonyTopper nie zmienia to faktu, że zamiennik dla separatora tysięcy jest zakodowany na stałe, do ,którego w niektórych lokalizacjach jest umieszczony separator przecinków
— Andreas

0

Zastąp przecinek pustym ciągiem:

var x = parseFloat("2,299.00".replace(",",""))
alert(x);


to nie jest bezpieczne. Jak wspomniano w innych odpowiedziach, w wielu częściach świata przecinki oznaczają przecinek.
— David Meister

1
Również ta metoda nie zadziała na 2299000,00, ponieważ zastąpiłaby tylko pierwszy przecinek
— wizulus

0

Jeśli masz mały zestaw języków do obsługi, prawdopodobnie lepiej by było, gdybyś po prostu zakodował kilka prostych reguł:

function parseNumber(str, locale) {
  let radix = ',';
  if (locale.match(/(en|th)([-_].+)?/)) {
    radix = '.';
  }
  return Number(str
    .replace(new RegExp('[^\\d\\' + radix + ']', 'g'), '')
    .replace(radix, '.'));
}

0
const parseLocaleNumber = strNum => {
    const decSep = (1.1).toLocaleString().substring(1, 2);
    const formatted = strNum
        .replace(new RegExp(`([${decSep}])(?=.*\\1)`, 'g'), '')
        .replace(new RegExp(`[^0-9${decSep}]`, 'g'), '');
    return Number(formatted.replace(decSep, '.'));
};

0
Number("2,299.00".split(',').join(''));   // 2299

Funkcja split dzieli ciąg znaków na tablicę, używając znaku „,” jako separatora i zwraca tablicę.
Funkcja join łączy elementy tablicy zwrócone przez funkcję split.
Funkcja Number () konwertuje połączony ciąg na liczbę.


Opowiedz trochę więcej o tym, jakie jest rozwiązanie i jak rozwiązuje problem.
— Tariq
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.