ฉันจะแยกวิเคราะห์สตริงที่มีตัวคั่นพันลูกน้ำเป็นตัวเลขได้อย่างไร


110

ฉันมี2,299.00เป็นสตริงและฉันพยายามแยกวิเคราะห์เป็นตัวเลข ฉันลองใช้parseFloatซึ่งผลลัพธ์เป็น 2 ฉันเดาว่าลูกน้ำคือปัญหา แต่ฉันจะแก้ปัญหานี้อย่างไรให้ถูกต้อง เพียงแค่ลบลูกน้ำ?

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

คำตอบ:


124

ใช่ลบเครื่องหมายจุลภาค:

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

7
ใช่ แต่ตอนนี้ตำแหน่งทศนิยมหายไป 2300.00 ผลลัพธ์ใน 2300 ตัวอย่างเช่น
user1540714

@ user1540714 นั่นเป็นเพราะมันลอยมากกว่าสตริง หากคุณต้องการส่งออกคุณต้องจัดรูปแบบให้แสดงทศนิยม 2 ตำแหน่งเสมอ
Jon Taylor

39
ฉันขอแนะนำให้เราจับเครื่องหมายจุลภาคทั้งหมดแทนโดยใช้: .replace(/,/g, '') regex นี้ใช้ global gเพื่อแทนที่ทั้งหมด
gdibble

36
ฉันไม่รู้ว่าทำไมคำตอบนี้จึงได้รับการโหวตเพิ่มขึ้นมากมายและถูกเลือกว่าถูกต้องมันมีสองประเด็นที่ร้ายแรง 1. มันไม่สามารถจัดการกับตัวเลขที่มีเครื่องหมายจุลภาคหลายตัวเช่นparseFloat("2,000,000.00".replace(',',''))ผลตอบแทน2000และ 2. มันล้มเหลวในหลายภูมิภาคของโลกที่,มีตำแหน่งทศนิยมเช่นparseFloat("2.000.000,00".replace(',',''))ผลตอบแทน2- ดูคำตอบของฉันด้านล่างสำหรับสิ่งที่ใช้ได้ทุกที่ในโลก
David Meister

1
ในภาษาฝรั่งเศสเครื่องหมายจุลภาคเป็นตัวคั่นทศนิยม ... ดังนั้นจึงล้มเหลวสำหรับสถานการณ์ที่เป็นไปได้มากที่เบราว์เซอร์ได้ตั้งค่าภาษาฝรั่งเศส
Syed Aqeel Ashiq

122

การลบเครื่องหมายจุลภาคอาจเป็นอันตรายได้เนื่องจากตามที่คนอื่น ๆ ได้กล่าวไว้ในความคิดเห็นหลายภาษาใช้เครื่องหมายจุลภาคเพื่อหมายถึงสิ่งที่แตกต่างกัน (เช่นตำแหน่งทศนิยม)

ฉันไม่รู้ว่าคุณเอาสายอักขระมาจากไหน แต่ในบางที่ในโลก"2,299.00"=2.299

Intlวัตถุที่จะได้รับเป็นวิธีที่ดีที่จะจัดการปัญหานี้ แต่อย่างใดพวกเขาก็พยายามที่จะจัดส่งข้อมูลจำเพาะมีเพียงIntl.NumberFormat.format()API และไม่มีparseคู่ :(

วิธีเดียวในการแยกวิเคราะห์สตริงที่มีอักขระตัวเลขทางวัฒนธรรมไปยังหมายเลขที่เครื่องจดจำได้ด้วยวิธีที่มีเหตุผลของ i18n คือการใช้ไลบรารีที่ใช้ประโยชน์จากข้อมูล CLDR เพื่อปกปิดวิธีที่เป็นไปได้ทั้งหมดในการจัดรูปแบบสตริงตัวเลขhttp: //cldr.unicode องค์กร /

ตัวเลือก JS ที่ดีที่สุดสองตัวที่ฉันเจอมาจนถึงตอนนี้:


5
ไม่สามารถเชื่อได้ว่าไม่มีใครโหวตคำตอบนี้มันเป็นคำตอบเดียวที่แท้จริงในหน้านี้
evilkos

11
เห็นด้วยอย่างยิ่งว่า Intl ควรมีคู่การแยกวิเคราะห์ ดูเหมือนชัดเจนว่าผู้คนต้องการสิ่งนี้
carlossless

1
นี่เป็นวิธีเดียวที่เป็นระบบในการดำเนินการนี้ ไม่สามารถบรรลุสิ่งนี้ได้ด้วย regex วิธีเดียวที่เหมาะกับทุกคน จุลภาคและจุดมีความหมายแตกต่างกันในภาษาต่างๆ
Wildhammer

41

ในเบราว์เซอร์ที่ทันสมัยคุณสามารถใช้Intl.NumberFormatในตัวเพื่อตรวจจับการจัดรูปแบบหมายเลขของเบราว์เซอร์และปรับอินพุตให้เป็นปกติ

function parseNumber(value, locales = navigator.languages) {
  const example = Intl.NumberFormat(locales).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 }`);
}

เห็นได้ชัดว่ามีห้องสำหรับการเพิ่มประสิทธิภาพและการแคช แต่ก็ใช้ได้ผลในทุกภาษา


ทำไมรายการนี้ถึงไม่ได้รับการโหวตเพิ่ม !!! เป็นโซลูชั่นที่หรูหราที่สุดสำหรับ INPUT ในรูปแบบสากล! ขอบคุณ!!
kpollock

ในสกุลเงินของฝรั่งเศสคือ 231123 413,12
stackdave

หากคุณต้องการการสนับสนุนสำหรับ IE11 ให้เปลี่ยนบรรทัดที่ 3 เป็น: const cleanPattern = new RegExp("[^-+0-9" + example.charAt( 1 ) + "]", 'g');- template strings "` "ไม่รองรับโดย IE - caniuse.com/#search=template%20string
ยาว

มันจะไม่ดีกว่าที่จะผ่านnavigator.languagesแทนการnavigator.languageที่จะสร้าง? ฉันสงสัยว่าเพราะในหนังสือ "JavaScript สมัยใหม่สำหรับคนใจร้อน" หน้า 180 แนะนำให้ใช้อันนั้น ไม่แน่ใจว่าความแตกต่างคืออะไร
Kohei Nozaki

1
@KoheiNozaki ฉันได้ดูการเจรจาสถานที่อีกครั้งและดูเหมือนว่าคุณกำลังทำอะไรบางอย่าง เบราว์เซอร์ควรต่อรองตำแหน่งที่ต้องการหากคุณส่งผ่านทั้งหมดดังนั้นจึงไม่ควรเจ็บและจะดีกว่าที่จะส่งต่อทั้งหมดเข้ามา
Paul Alexander

26

ลบสิ่งที่ไม่ใช่ตัวเลขจุดทศนิยมหรือเครื่องหมายลบ ( -):

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

ปรับปรุงซอ

โปรดทราบว่าจะใช้ไม่ได้กับตัวเลขในสัญกรณ์วิทยาศาสตร์ หากคุณต้องการให้เปลี่ยนreplaceสายเพื่อเพิ่มe, Eและ+รายชื่อของตัวละครที่ได้รับการยอมรับไปนี้:

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

4
สิ่งนี้ใช้ไม่ได้กับจำนวนลบหรือตัวเลขในสัญกรณ์วิทยาศาสตร์
Aadit M Shah

1
str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1"); นี่คือสิ่งที่ฉันจะใช้ แต่ฉันใช้ regex อย่างไร้ประโยชน์และเป็นสิ่งที่ฉันพบในขณะที่กลับมาในเธรด SO อื่น ๆ
Jon Taylor

@ จอนเทย์เลอร์: เป้าหมายที่นี่ไม่ได้อยู่ที่การตรวจสอบหมายเลขเพียงเพื่อให้มันใช้งานได้parseFloat- ซึ่งจะตรวจสอบความถูกต้อง :-)
TJ Crowder

ที่ของฉันทำเท่าที่ฉันรู้ ฉันใช้สิ่งนี้เพื่อแปลง 1,234,567 และอื่น ๆ เป็น 1234567 อย่างที่ฉันพูดไปแม้ว่าฉันจะใช้ regex อย่างไร้ประโยชน์ดังนั้นฉันไม่สามารถบอกคุณได้ตลอดชีวิตว่ามันทำอะไรได้บ้างฮ่า ๆ
Jon Taylor

1
ความคิดที่ไม่ดีในการลบเครื่องหมาย "-" - รับหมายเลขอื่น ๆ ที่สมบูรณ์และหากคุณแยกวิเคราะห์รูปแบบที่แตกต่างกัน "7.500"! == "7,500"
เสิร์จ

15

โดยปกติคุณควรพิจารณาใช้ช่องป้อนข้อมูลซึ่งไม่อนุญาตให้ป้อนข้อความฟรีสำหรับค่าตัวเลข แต่อาจมีบางกรณีที่คุณต้องเดารูปแบบการป้อนข้อมูล ตัวอย่างเช่น 1.234,56 ในเยอรมนีหมายถึง 1,234.56 ในสหรัฐอเมริกา ดูhttps://salesforce.stackexchange.com/a/21404สำหรับรายชื่อประเทศที่ใช้ลูกน้ำเป็นทศนิยม

ฉันใช้ฟังก์ชั่นต่อไปนี้เพื่อทำการเดาที่ดีที่สุดและตัดอักขระที่ไม่ใช่ตัวเลขทั้งหมดออก:

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

ลองดูที่นี่: https://plnkr.co/edit/9p5Y6H?p=preview

ตัวอย่าง:

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

ฟังก์ชันนี้มีจุดอ่อนอย่างหนึ่ง: ไม่สามารถคาดเดารูปแบบได้หากคุณมี 1,123 หรือ 1.123 เนื่องจากขึ้นอยู่กับรูปแบบโลแคลทั้งคู่อาจเป็นเครื่องหมายจุลภาคหรือตัวคั่นหลักพัน ในกรณีพิเศษนี้ฟังก์ชันจะถือว่าตัวคั่นเป็นตัวคั่นหลักพันและส่งคืน 1123


มันล้มเหลวสำหรับตัวเลขเช่น 1,111.11 ซึ่งเห็นได้ชัดว่าเป็นรูปแบบภาษาอังกฤษ แต่ส่งกลับ 111111
Mr.Goferito

ขอบคุณคุณ Goferito - ฉันขอโทษ - ฉันแก้ไขฟังก์ชันแล้ว
Gerfried

ดูเหมือนว่าสิ่งนี้อาจล้มเหลวสำหรับตัวเลขขนาดเล็กมาก "0,124" ในภาษาฝรั่งเศสเช่น
Paul Alexander

ว้าวเยี่ยม! เกือบจะตรงตามสิ่งที่ฉันกำลังมองหา: 3,00 €ยังถูกแทนที่เป็น 3.00 เพียงแค่ทราบว่ามีการจัดรูปแบบให้3,001 3001เพื่อหลีกเลี่ยงปัญหานี้อินพุตควรเป็นสัญลักษณ์ทศนิยมเสมอ เช่น3,001.00€ 3,001.00แปลงอย่างถูกต้อง นอกจากนี้โปรดอัปเดต jsfiddle 0,124ยังคงแปลงเป็น 124
Arnis Juraga

เพื่อนที่ทำได้ดีฉันคิดว่ามันคุ้มค่าที่จะเป็นคำตอบที่ถูกต้อง
Waheed

5

มันน่างงที่พวกเขารวมtoLocaleStringแต่ไม่ใช่วิธีการแยกวิเคราะห์ อย่างน้อยtoLocaleString ที่ไม่มีอาร์กิวเมนต์ได้รับการสนับสนุนอย่างดีใน IE6 +

สำหรับi18nโซลูชันฉันคิดสิ่งนี้:

ขั้นแรกให้ตรวจหาตัวคั่นทศนิยมตำแหน่งที่ตั้งของผู้ใช้:

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

จากนั้นปรับตัวเลขให้เป็นปกติหากมีตัวคั่นทศนิยมมากกว่าหนึ่งตัวในสตริง:

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

สุดท้ายลบสิ่งที่ไม่ใช่ตัวเลขหรือตัวคั่นทศนิยม:

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

5

นี่คือกระดาษห่อหุ้มที่เรียบง่ายไม่สร้างความรำคาญรอบ ๆparseFloatฟังก์ชัน

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 โพสต์และคุณแน่ใจเกี่ยวกับจำนวนตำแหน่งทศนิยมคุณสามารถแทนที่จุดและลูกน้ำทั้งหมดแล้วหารด้วย 100 เช่น:

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

หรือถ้าคุณมีทศนิยม 3 ตำแหน่ง

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

ขึ้นอยู่กับคุณว่าคุณต้องการใช้ parseInt, parseFloat หรือ Number นอกจากนี้หากคุณต้องการเก็บจำนวนตำแหน่งทศนิยมคุณสามารถใช้ฟังก์ชัน. toFixed (... )


1

คำตอบทั้งหมดนี้จะล้มเหลวหากคุณมีตัวเลขเป็นล้าน

3,456,789 จะคืนค่า 3456 ด้วยวิธีการแทนที่

คำตอบที่ถูกต้องที่สุดสำหรับการลบเครื่องหมายจุลภาคจะต้องเป็น

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

หรือเขียนง่ายๆ.

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

แน่นอนว่าชาวอเมริกันใช้ลูกน้ำไม่ใช่จุด คงเป็นเรื่องโง่เขลาที่จะพยายามสร้างความโหดร้ายเพื่อพยายามจัดการทั้งสองอย่าง
กรณี

2
แต่ "ความชั่วร้าย" ดังกล่าวมีอยู่แล้วซึ่งเป็นส่วนหนึ่งของสิ่งที่ Unicode มีให้ (ดูคำตอบของฉัน) ฉันแน่ใจว่าคุณจะรู้สึกโง่น้อยลงในเรื่องนี้หากคุณบริหาร บริษัท ที่มีลูกค้าต่างชาติ
David Meister

1

สิ่งนี้จะแปลงตัวเลขในภาษาใดก็ได้ให้เป็นตัวเลขปกติ ใช้ได้กับจุดทศนิยมเช่นกัน:

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

ด้วยฟังก์ชันนี้คุณจะสามารถจัดรูปแบบค่าในหลายรูปแบบเช่น1.234,56และ1,234.56และแม้จะมีข้อผิดพลาดเช่น1.234.56และ1,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

หากคุณต้องการคำตอบ l10n ให้ทำด้วยวิธีนี้ ตัวอย่างใช้สกุลเงิน แต่คุณไม่ต้องการสิ่งนั้น ห้องสมุดนานาชาติจะต้องได้รับการเติมเต็มหากคุณต้องรองรับเบราว์เซอร์รุ่นเก่า

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
นี่ไม่ใช่คำตอบ l10n จริงๆเพราะสำหรับบางภาษา (เช่น DE) ลูกน้ำคือจุดทศนิยม
Andreas

และยังreplace()แทนที่เฉพาะการจับคู่แรกเมื่อไม่ได้ใช้RegExpกับ'g'ธง
binki

@binki ขอบคุณ แก้ไขแล้ว.
Tony Topper

@Andreas นี่คือคำตอบ l10n หากคุณต้องการสกุลเงินอื่นคุณเพียงแค่เปลี่ยนค่าของ currencyId เป็นประเทศนั้น currencyId
Tony Topper

1
@TonyTopper สิ่งนี้ไม่ได้เปลี่ยนความจริงที่ว่าการแทนที่สำหรับตัวคั่นหลักพันนั้นถูกเข้ารหัส,ซึ่งอยู่ในตัวคั่นด้วยเครื่องหมายจุลภาคในบางภาษา
Andreas

0

แทนที่เครื่องหมายจุลภาคด้วยสตริงว่าง:

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


สิ่งนี้ไม่ปลอดภัย ดังที่กล่าวไว้ในคำตอบอื่น ๆ ในหลายส่วนของโลกเครื่องหมายจุลภาคแสดงถึงจุดทศนิยม
David Meister

1
นอกจากนี้วิธีนี้จะใช้ไม่ได้กับ 2,299,000.00 เนื่องจากจะแทนที่จุลภาคตัวแรกเท่านั้น
wizulus

0

หากคุณมีสถานที่ตั้งเล็ก ๆ เพื่อรองรับคุณอาจจะดีกว่าโดยการเข้ารหัสกฎง่ายๆสองสามข้อ:

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

ฟังก์ชันแยกจะแยกสตริงออกเป็นอาร์เรย์โดยใช้ "," เป็นตัวคั่นและส่งกลับอาร์เรย์
ฟังก์ชัน join จะรวมองค์ประกอบของอาร์เรย์ที่ส่งคืนจากฟังก์ชันแยก
ฟังก์ชัน Number () แปลงสตริงที่รวมเป็นตัวเลข


โปรดอธิบายเพิ่มเติมอีกเล็กน้อยว่าวิธีแก้ปัญหาคืออะไรและจะแก้ปัญหาได้อย่างไร
Tariq
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.