Làm cách nào để tôi có thể phân tích cú pháp một chuỗi có dấu phân cách phần nghìn thành một số?


103

Tôi có 2,299.00dưới dạng một chuỗi và tôi đang cố gắng phân tích cú pháp nó thành một số. Tôi đã thử sử dụng parseFloat, kết quả là 2. Tôi đoán dấu phẩy là vấn đề, nhưng làm cách nào để giải quyết vấn đề này một cách đúng đắn? Chỉ cần bỏ dấu phẩy?

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

Câu trả lời:


121

Có xóa dấu phẩy:

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

7
Vâng, nhưng bây giờ các chữ số thập phân bị mất. 2300.00 cho kết quả là 2300 chẳng hạn.
user1540714

@ user1540714 đó là vì nó là float chứ không phải là một chuỗi. Nếu sau đó bạn cần xuất ra, bạn cần định dạng để nó luôn hiển thị 2 dấu thập phân.
Jon Taylor,

Điều đó có thể tránh được không? I’ll try toFixed
user1540714

38
Tôi có thể gợi ý rằng chúng ta nên nắm bắt tất cả các dấu phẩy bằng cách sử dụng: .replace(/,/g, '') Regex này sử dụng global gđể thay thế-all.
gdibble

32
Tôi không hiểu tại sao câu trả lời này lại nhận được rất nhiều phiếu tán thành và được chọn là đúng, nó có hai vấn đề nghiêm trọng. 1. Nó không thể xử lý các số có nhiều dấu phẩy, ví dụ như parseFloat("2,000,000.00".replace(',',''))trả về 2000và 2. nó không thành công ở nhiều khu vực trên thế giới nơi ,có chữ số thập phân, ví dụ: parseFloat("2.000.000,00".replace(',',''))trả về 2- hãy xem câu trả lời của tôi bên dưới để biết thứ gì đó hoạt động ở mọi nơi trên thế giới.
David Meister

113

Việc xóa dấu phẩy có khả năng gây nguy hiểm vì như những người khác đã đề cập trong các nhận xét, nhiều ngôn ngữ sử dụng dấu phẩy để có nghĩa khác (như chữ số thập phân).

Tôi không biết bạn lấy chuỗi của mình từ đâu, nhưng ở một số nơi trên thế giới "2,299.00"=2.299

Đối Intltượng có thể là một cách hay để giải quyết vấn đề này, nhưng bằng cách nào đó họ đã quản lý để gửi thông số kỹ thuật chỉ với một Intl.NumberFormat.format()API và không có parseđối tác :(

Cách duy nhất để phân tích cú pháp một chuỗi với các ký tự số văn hóa trong đó thành một số có thể nhận dạng được bằng bất kỳ cách nào của i18n là sử dụng thư viện tận dụng dữ liệu CLDR để che đi tất cả các cách định dạng chuỗi số có thể có . Http: //cldr.unicode. org /

Hai tùy chọn JS tốt nhất mà tôi đã xem xét cho đến nay:


4
Không thể tin rằng chưa có ai ủng hộ câu trả lời này, đó là câu trả lời thực tế duy nhất trên trang này!
evilkos

9
Hoàn toàn đồng ý rằng Intl nên có một đối tác phân tích cú pháp. Rõ ràng là mọi người sẽ cần điều này.
carlossless

Đây là cách duy nhất có hệ thống để làm điều này. Không thể đạt được điều này với một cách tiếp cận phù hợp với tất cả các regex. Dấu phẩy và dấu chấm có ý nghĩa khác nhau trong các ngôn ngữ khác nhau.
Wildhammer

38

Trên các trình duyệt hiện đại, bạn có thể sử dụng Intl.NumberFormat tích hợp sẵn để phát hiện định dạng số của trình duyệt và chuẩn hóa đầu vào cho khớp.

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

Rõ ràng là có chỗ cho một số tối ưu hóa và bộ nhớ đệm nhưng điều này hoạt động đáng tin cậy ở tất cả các ngôn ngữ.


Tại sao điều này không có một lượng ủng hộ !!! Đây là giải pháp thanh lịch nhất để INPUT ở các định dạng quốc tế! Cảm ơn bạn!!
kpollock

ở Pháp tệ là 231 123 413,12
stackdave

26

Xóa bất kỳ thứ gì không phải là chữ số, dấu thập phân hoặc dấu trừ ( -):

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

Cập nhật fiddle

Lưu ý rằng nó sẽ không hoạt động đối với các số trong ký hiệu khoa học. Nếu bạn muốn nó, thay đổi replacedòng để thêm e, E+vào danh sách các nhân vật có thể chấp nhận:

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

4
Điều này sẽ không hoạt động đối với số âm hoặc số trong ký hiệu khoa học.
Aadit M Shah,

1
str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1"); Đây là những gì tôi sẽ sử dụng nhưng tôi hoàn toàn vô dụng với regex và là thứ mà tôi đã tìm thấy một thời gian trở lại trên một số luồng SO khác.
Jon Taylor

@JonTaylor: Mục tiêu ở đây không phải để xác thực số, chỉ để làm cho nó hoạt động parseFloat- điều này sẽ xác thực nó. :-)
TJ Crowder

cái của tôi làm, theo như tôi biết. Tôi sử dụng điều này để chuyển đổi 1.234.567 vv thành 1234567. Như tôi đã nói, mặc dù tôi hoàn toàn vô dụng với regex vì vậy tôi không thể cho bạn biết nó thực sự làm gì được lol.
Jon Taylor

1
ý tưởng tồi khi xóa "-" dấu trừ - lấy hoàn toàn số khác và cũng có thể nếu bạn phân tích cú pháp các định dạng không đồng nhất, "7.500"! == "7.500"
Serge

14

Thông thường, bạn nên cân nhắc sử dụng các trường nhập không cho phép nhập văn bản miễn phí cho các giá trị số. Nhưng có thể có những trường hợp, khi bạn cần đoán định dạng đầu vào. Ví dụ: 1.234,56 ở Đức nghĩa là 1.234,56 ở Mỹ. Xem https://salesforce.stackexchange.com/a/21404 để biết danh sách các quốc gia sử dụng dấu phẩy dưới dạng số thập phân.

Tôi sử dụng hàm sau để phỏng đoán chính xác nhất và loại bỏ tất cả các ký tự không phải số:

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

Hãy thử nó tại đây: https://plnkr.co/edit/9p5Y6H?p=preview

Ví dụ:

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

Hàm có một điểm yếu: Không thể đoán định dạng nếu bạn có 1,123 hoặc 1,123 - vì tùy thuộc vào định dạng ngôn ngữ, cả hai có thể là dấu phẩy hoặc dấu phân cách hàng nghìn. Trong trường hợp đặc biệt này, hàm sẽ coi dấu phân tách là dấu phân cách hàng nghìn và trả về 1123.


Nó không cho con số như 1,111.11, mà rõ ràng là định dạng tiếng Anh, nhưng lợi nhuận 111.111
Ông Goferito

Cảm ơn ông Goferito - Tôi xin lỗi - Tôi đã sửa chức năng này.
Gerfried

Có vẻ như điều này cũng có thể không thành công đối với các số rất nhỏ "0,124" trong ngôn ngữ Pháp chẳng hạn.
Paul Alexander

Wou, tuyệt vời! Điều này gần như chính xác, những gì tôi đang tìm kiếm: 3,00 €cũng được thay thế thành 3,00. Chỉ cần một ghi chú, 3,001được định dạng thành 3001. Để tránh điều này, đầu vào phải luôn có ký hiệu thập phân. Vd: 3,001.00€ 3,001.00chuyển đổi chính xác. Ngoài ra, hãy cập nhật jsfiddle. 0,124có vẫn chuyển đổi sang 124
Arnis Juraga

làm tốt lắm bạn ơi, tôi nghĩ xứng đáng để trở thành câu trả lời chính xác
Waheed

5

Thật khó hiểu khi họ bao gồm một toLocaleString nhưng không bao gồm một phương thức phân tích cú pháp. Ít nhất toLocaleString không có đối số được hỗ trợ tốt trong IE6 +.

Đối với giải pháp i18n , tôi đã nghĩ ra điều này:

Đầu tiên phát hiện dấu phân tách thập phân theo ngôn ngữ của người dùng:

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

Sau đó, chuẩn hóa số nếu có nhiều hơn một dấu phân tách thập phân trong Chuỗi:

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

Cuối cùng, xóa bất kỳ thứ gì không phải là số hoặc dấu phân cách thập phân:

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

5

Đây là một trình bao bọc đơn giản, không phô trương xung quanh parseFloathàm.

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

Nếu bạn muốn tránh vấn đề mà David Meister đã đăng và bạn chắc chắn về số chữ số thập phân, bạn có thể thay thế tất cả các dấu chấm và dấu phẩy và chia cho 100, ví dụ:

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

hoặc nếu bạn có 3 số thập phân

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

Tùy thuộc vào bạn nếu bạn muốn sử dụng parseInt, parseFloat hay Number. Ngoài ra Nếu bạn muốn giữ nguyên số chữ số thập phân, bạn có thể sử dụng hàm .toFixed (...).


1

Tất cả các câu trả lời này đều thất bại nếu bạn có một con số hàng triệu.

3,456,789 sẽ chỉ trả về 3456 với phương thức thay thế.

Câu trả lời chính xác nhất cho việc loại bỏ dấu phẩy đơn giản sẽ phải là.

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

Hoặc đơn giản là viết.

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

Tất nhiên, người Mỹ sử dụng dấu phẩy chứ không phải dấu chấm. Sẽ thật ngu ngốc nếu cố làm ra một trò quái dị để cố gắng xử lý cả hai.
Trường hợp

2
nhưng sự "quái dị" như vậy đã tồn tại như một phần của những gì Unicode cung cấp (xem câu trả lời của tôi). Tôi chắc rằng bạn sẽ ít cảm thấy ngu ngốc hơn về điều này nếu bạn điều hành một công ty có khách hàng quốc tế.
David Meister

1

Điều này chuyển đổi một số ở bất kỳ ngôn ngữ nào thành số bình thường. Cũng hoạt động cho các điểm thập phân:

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

Với chức năng này, bạn sẽ có thể định dạng các giá trị ở nhiều định dạng như 1.234,561,234.56, và thậm chí với các lỗi như 1.234.561,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

Nếu bạn muốn có câu trả lời l10n, hãy làm theo cách này. Ví dụ sử dụng tiền tệ, nhưng bạn không cần điều đó. Thư viện Intl sẽ cần được điền đầy đủ nếu bạn phải hỗ trợ các trình duyệt cũ hơn.

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
đây không thực sự là câu trả lời l10n, vì đối với một số ngôn ngữ (ví dụ: DE), dấu phẩy là dấu thập phân.
Andreas

Và cũng replace()chỉ thay thế trận đấu đầu tiên khi không sử dụng RegExpvới 'g'cờ.
binki

@binki Cảm ơn. Đã sửa.
Tony Topper

@Andreas Đây là câu trả lời của l10n. Nếu bạn muốn một loại tiền tệ khác, bạn chỉ cần thay đổi giá trị của currencyId thành tiền tệ của quốc gia đó.
Tony Topper,

1
@TonyTopper này không làm thay đổi thực tế là thay thế cho dấu định giới ngàn được hardcoded ,mà là ở dấu phẩy seperator ở một số vùng
Andreas

0

Thay thế dấu phẩy bằng một chuỗi trống:

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


điều này không an toàn. Như đã đề cập ở các câu trả lời khác, ở nhiều nơi trên thế giới, dấu phẩy biểu thị dấu thập phân.
David Meister

1
Ngoài ra phương pháp này sẽ không hoạt động trên 2,299,000.00 vì nó sẽ chỉ thay thế các dấu phẩy đầu tiên
wizulus

0

Nếu bạn có một tập hợp nhỏ các ngôn ngữ để hỗ trợ, bạn có thể tốt hơn bằng cách mã hóa cứng một vài quy tắc đơn giản:

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

Hàm chia tách chuỗi thành một mảng bằng cách sử dụng "," làm dấu phân tách và trả về một mảng.
Hàm nối sẽ nối các phần tử của mảng được trả về từ hàm tách.
Hàm Number () chuyển đổi chuỗi đã nối thành một số.


Vui lòng giải thích thêm một chút về giải pháp là gì và nó giải quyết vấn đề như thế nào.
Tariq
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.