Virgül binlik ayırıcısı olan bir dizeyi bir sayıya nasıl ayrıştırabilirim?


106

Ben 2,299.00bir dize olarak ve ben bir sayıya ayrıştırmak çalışıyorum. Kullanmayı denedim parseFloat, bu da 2 sonucunu veriyor. Sanırım sorun virgül, ama bu sorunu doğru şekilde nasıl çözerim? Virgül kaldırılsın mı?

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

Yanıtlar:


120

Evet, virgülleri kaldırın:

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

7
Evet, ama şimdi ondalık basamaklar kayboldu. 2300.00, örneğin 2300 sonucunu verir.
user1540714

@ user1540714 thats çünkü onun bir dizeden ziyade bir kayan nokta. Daha sonra çıktısını almanız gerekirse, her zaman 2 ondalık nokta gösterecek şekilde biçimlendirmeniz gerekir.
Jon Taylor

38
Bunun yerine, şunu kullanarak tüm virgülleri yakalamayı önerebilir miyim: .replace(/,/g, '') Bu normal ifade, gtümünü değiştirmek için global'i kullanır .
gdibble

33
Bu cevabın neden bu kadar çok oy aldığını ve doğru olarak seçildiğini bilmiyorum, iki ciddi sorunu var. 1. Birden çok virgül içeren sayıları işleyemez, ör. parseFloat("2,000,000.00".replace(',',''))Döner 2000ve 2. ,ondalık basamak olan dünyanın birçok bölgesinde başarısız olur , ör. parseFloat("2.000.000,00".replace(',',''))Dönüşler 2- dünyanın her yerinde işe yarayan bir şey için aşağıdaki cevabıma bakın.
David Meister

1
Fransız yerel ayarında virgül ondalık ayırıcıdır ... Dolayısıyla bu, bir tarayıcının Fransız yerel
ayarını

115

Virgüllerin kaldırılması potansiyel olarak tehlikelidir, çünkü diğerlerinin yorumlarda da belirttiği gibi, birçok yerel ayar, farklı bir şeyi ifade etmek için virgül kullanır (ondalık basamak gibi).

İpini nereden aldığını bilmiyorum ama dünyanın bazı yerlerinde "2,299.00"=2.299

IntlNesne bu sorunu çözmek için güzel bir yol olabilirdi, ama nedense onlar sadece ile spec gemi başardı Intl.NumberFormat.format()API ve hiçbir parsemeslektaşı :(

İçinde kültürel sayısal karakterler bulunan bir dizeyi, herhangi bir i18n mantıklı yolla makinede tanınabilir bir sayıya ayrıştırmanın tek yolu, http: //cldr.unicode sayı dizelerini biçimlendirmenin tüm olası yollarını örtmek için CLDR verilerinden yararlanan bir kitaplık kullanmaktır . org /

Şimdiye kadar karşılaştığım en iyi iki JS seçeneği:


4
Henüz kimsenin bu cevaba oy vermediğine inanamıyorum, bu sayfadaki tek gerçek cevap bu!
evilkos

9
Intl'nin bir ayrıştırma muadili olması gerektiğine tamamen katılıyorum. İnsanların buna ihtiyacı olacağı açık görünüyor.
carlossless

Bunu yapmanın tek sistematik yolu budur. Bunu tek bir normal ifade yaklaşımıyla başaramazsınız. Virgül ve noktaların farklı dillerde farklı anlamları vardır.
Wildhammer

39

Modern tarayıcılarda , tarayıcının sayı biçimlendirmesini algılamak ve girişi eşleşecek şekilde normalleştirmek için yerleşik Uluslararası Sayı Biçimi'ni kullanabilirsiniz.

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 }`);
}

Belli ki bazı optimizasyon ve önbelleğe alma için yer var, ancak bu tüm dillerde güvenilir şekilde çalışıyor.


Neden bunda bir sürü olumlu oy yok !!! Uluslararası formatlarda INPUT'a en zarif çözümdür! Teşekkür ederim!!
kpollock

Fransa para birimi 231123 413,12
stackdave

IE11 için desteğe ihtiyacınız varsa, 3. satırı şu şekilde değiştirin: const cleanPattern = new RegExp("[^-+0-9" + example.charAt( 1 ) + "]", 'g');- şablon dizeleri "" "IE tarafından desteklenmez - caniuse.com/#search=template%20string
uzun

26

Rakam, ondalık nokta veya eksi işareti ( -) olmayan her şeyi kaldırın :

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);

Güncellenmiş keman

Bilimsel gösterimdeki sayılar için çalışmayacağını unutmayın. Eğer isterseniz, değiştirmek replaceeklemek hattı e, Eve +kabul edilebilir karakter listesine:

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

4
Bu bilimsel gösterimdeki negatif sayılar veya sayılar için işe yaramaz.
Aadit M Shah

1
str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1"); Kullanacağım şey bu ama regex'te tamamen işe yaramıyorum ve bir süre önce başka bir SO iş parçacığında bulduğum bir şey.
Jon Taylor

@JonTaylor: Buradaki amaç numarayı doğrulamak değildi , sadece onun için çalışmasını sağlamak parseFloat- bu da onu doğrulayacak. :-)
TJ Crowder

Bildiğim kadarıyla benimki de yapıyor. Bunu 1.234.567 vb. 1234567'ye dönüştürmek için kullanıyorum. Dediğim gibi regex'te tamamen işe yaramaz olsam da hayatım boyunca size lol'in gerçekte ne yaptığını söyleyemedim.
Jon Taylor

1
"-" eksi işaretini kaldırmak için kötü bir fikir - tamamen başka bir sayı elde edin ve ayrıca heterojen biçimleri ayrıştırırsanız, "7.500"! == "7.500"
Serge

14

Genellikle, sayısal değerler için serbest metin girişine izin vermeyen giriş alanlarını kullanmayı düşünmelisiniz. Ancak giriş formatını tahmin etmeniz gereken durumlar olabilir. Örneğin Almanya'da 1.234,56, ABD'de 1.234.56 anlamına gelir. Ondalık olarak virgül kullanan ülkelerin listesi için https://salesforce.stackexchange.com/a/21404 adresine bakın .

En iyi tahminde bulunmak ve sayısal olmayan tüm karakterleri çıkarmak için aşağıdaki işlevi kullanıyorum:

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);
}   

Burada deneyin: https://plnkr.co/edit/9p5Y6H?p=preview

Örnekler:

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

İşlevin bir zayıf noktası vardır: 1,123 veya 1,123'e sahipseniz biçimi tahmin etmek mümkün değildir - çünkü yerel biçim biçimine bağlı olarak her ikisi de virgül veya binlik ayırıcı olabilir. Bu özel durumda, işlev ayırıcıyı binlik ayırıcı olarak ele alır ve 1123 döndürür.


Açıkçası İngilizce formatta olan 1,111.11 gibi sayılar için başarısız olur, ancak
111111'i

Teşekkür ederim, Bay Goferito - Özür dilerim - işlevi düzelttim.
Gerfried

Görünüşe göre bu, örneğin fransızca yerel ayarında çok küçük sayılar "0,124" için de başarısız olabilir.
Paul Alexander

Harika! Bu neredeyse tam olarak aradığım şey: 3,00 €3.00 ile değiştirildi. 3,001Biçimlendirilmiş bir not 3001. Bunu önlemek için, giriş her zaman ondalık sembollerle yapılmalıdır. Örneğin 3,001.00€ 3,001.00, doğru şekilde dönüştürür. Ayrıca, lütfen jsfiddle'ı güncelleyin. 0,124hala 124
Arnis Juraga

aferin dostum bence doğru cevap olmaya değer
Waheed

5

Bir toLocaleString içermeleri ancak bir ayrıştırma yöntemi içermeleri şaşırtıcı . En azından argümansız toLocaleString , IE6 + 'da iyi desteklenir.

Bir i18n çözümü için şunu buldum:

Önce kullanıcının yerel ondalık ayırıcısını bulun:

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

Ardından, Dizede birden fazla ondalık ayırıcı varsa sayıyı normalleştirin:

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

Son olarak, sayı veya ondalık ayırıcı olmayan her şeyi kaldırın:

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

5

Bu, parseFloatişlev etrafında basit, göze batmayan bir sarmalayıcıdır .

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

David Meister'ın yayınladığı sorundan kaçınmak istiyorsanız ve ondalık basamakların sayısından eminseniz, tüm noktaları ve virgülleri değiştirebilir ve 100'e bölebilirsiniz, örn .:

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

veya 3 ondalık sayıya sahipseniz

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

ParseInt, parseFloat veya Number kullanmak isteyip istemediğiniz size kalmış. Ayrıca ondalık basamak sayısını korumak istiyorsanız .toFixed (...) işlevini kullanabilirsiniz.


1

Milyonlarda bir sayıya sahipseniz tüm bu cevaplar başarısız olur.

3.456.789, 3456 'yi değiştirme yöntemiyle döndürür.

Virgüllerin kaldırılması için en doğru cevap şu olmalıdır.

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

Veya basitçe yazılmış.

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

Elbette Amerikalılar nokta değil virgül kullanıyor. İkisini birden halletmeye çalışmak için bir canavarlık yapmaya çalışmak aptallık olur.
Dava

2
ancak böyle bir "canavarlık", Unicode'un sağladığı şeyin bir parçası olarak zaten var (cevabıma bakın). Eminim uluslararası müşterilerle bir şirket yönetirseniz bu konuda daha az aptal hissedersiniz.
David Meister

1

Bu, yerel ayardaki bir sayıyı normal sayıya dönüştürür. Ondalık sayılar için de işe yarar:

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
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, '.'));
};

1

Bu fonksiyon ile benzeri birden fazla biçimde değerlerini biçimlendirmek mümkün olacak 1.234,56ve 1,234.56hatta olduğu gibi hatalar 1.234.56ve1,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

Bir 10n cevabı istiyorsanız, bu şekilde yapın. Örnek para birimi kullanıyor, ancak buna ihtiyacınız yok. Eski tarayıcıları desteklemeniz gerekiyorsa, uluslararası kitaplığın çoklu doldurulması gerekecektir.

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
bu gerçekten l10n cevabı değildir, çünkü bazı yerel ayarlar için (örneğin DE) virgül ondalık noktadır.
Andreas

Ve ayrıca replace()kullanmadığınız zaman sadece ilk maç değiştirir RegExpile 'g'bayrak.
binki

@binki Teşekkürler. Sabit.
Tony Topper

@Andreas Bu l10n cevabı. Başka bir para birimi istiyorsanız, sadece currencyId'nin değerini o ülke currencyId olarak değiştirirsiniz.
Tony Topper

1
@TonyTopper bu, ,bazı yerel ayarlarda virgül ayırıcıda bulunan binlik ayırıcı yerine kodunun sabit olduğu gerçeğini değiştirmez
Andreas

0

Virgülü boş bir dizeyle değiştirin:

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


bu güvenli değil. Diğer yanıtlarda da belirtildiği gibi, dünyanın birçok yerinde virgüller bir ondalık noktayı temsil eder.
David Meister

1
Ayrıca bu yöntem sadece ilk virgülün yerini alacağı için 2,299,000.00 üzerinde çalışmaz
wizulus

0

Desteklenecek küçük bir yerel ayar kümeniz varsa, birkaç basit kuralı kodlayarak muhtemelen daha iyi durumda olursunuz:

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

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

Bölme işlevi, dizeyi ayırıcı olarak "," kullanarak bir diziye böler ve bir dizi döndürür.
Join işlevi, split işlevinden döndürülen dizinin öğelerini birleştirir.
Number () işlevi, birleştirilmiş dizeyi bir sayıya dönüştürür.


Lütfen çözümün ne olduğu ve sorunu nasıl çözdüğü konusunda biraz daha detaylandırın.
Tarık
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.