ฉันต้องการ จำกัด อินพุตใน TextField เป็นจำนวนเต็ม คำแนะนำใด ๆ?
ฉันต้องการ จำกัด อินพุตใน TextField เป็นจำนวนเต็ม คำแนะนำใด ๆ?
คำตอบ:
เธรดเก่ามาก แต่ดูเหมือนว่าจะดีกว่าและตัดอักขระที่ไม่ใช่ตัวเลขออกหากวาง
// force the field to be numeric only
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
}
});
\\D+
(หรือเฉยๆ\\D
) แทน[^\\d]
หากคุณต้องการบันทึกอักขระสองสามตัว
Integer.parseInt(newValue)
และใช้งานtry
และcatch
ตรวจพบข้อผิดพลาดบนNumberFormatException
อัปเดต เม.ย. 2559
คำตอบนี้สร้างขึ้นเมื่อหลายปีก่อนและคำตอบเดิมส่วนใหญ่ล้าสมัยแล้ว
ตั้งแต่ Java 8u40 Java จึงมีTextFormatterซึ่งมักจะดีที่สุดสำหรับการบังคับใช้อินพุตของรูปแบบเฉพาะเช่นตัวเลขบน JavaFX TextFields:
ดูคำตอบอื่น ๆ สำหรับคำถามนี้ซึ่งกล่าวถึง TextFormatter โดยเฉพาะ
คำตอบเดิม
มีตัวอย่างบางส่วนในส่วนสำคัญนี้ฉันได้ทำซ้ำหนึ่งในตัวอย่างด้านล่าง:
// helper text field subclass which restricts text input to a given range of natural int numbers
// and exposes the current numeric int value of the edit box as a value property.
class IntField extends TextField {
final private IntegerProperty value;
final private int minValue;
final private int maxValue;
// expose an integer value property for the text field.
public int getValue() { return value.getValue(); }
public void setValue(int newValue) { value.setValue(newValue); }
public IntegerProperty valueProperty() { return value; }
IntField(int minValue, int maxValue, int initialValue) {
if (minValue > maxValue)
throw new IllegalArgumentException(
"IntField min value " + minValue + " greater than max value " + maxValue
);
if (maxValue < minValue)
throw new IllegalArgumentException(
"IntField max value " + minValue + " less than min value " + maxValue
);
if (!((minValue <= initialValue) && (initialValue <= maxValue)))
throw new IllegalArgumentException(
"IntField initialValue " + initialValue + " not between " + minValue + " and " + maxValue
);
// initialize the field values.
this.minValue = minValue;
this.maxValue = maxValue;
value = new SimpleIntegerProperty(initialValue);
setText(initialValue + "");
final IntField intField = this;
// make sure the value property is clamped to the required range
// and update the field's text to be in sync with the value.
value.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
if (newValue == null) {
intField.setText("");
} else {
if (newValue.intValue() < intField.minValue) {
value.setValue(intField.minValue);
return;
}
if (newValue.intValue() > intField.maxValue) {
value.setValue(intField.maxValue);
return;
}
if (newValue.intValue() == 0 && (textProperty().get() == null || "".equals(textProperty().get()))) {
// no action required, text property is already blank, we don't need to set it to 0.
} else {
intField.setText(newValue.toString());
}
}
}
});
// restrict key input to numerals.
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
@Override public void handle(KeyEvent keyEvent) {
if(intField.minValue<0) {
if (!"-0123456789".contains(keyEvent.getCharacter())) {
keyEvent.consume();
}
}
else {
if (!"0123456789".contains(keyEvent.getCharacter())) {
keyEvent.consume();
}
}
}
});
// ensure any entered values lie inside the required range.
this.textProperty().addListener(new ChangeListener<String>() {
@Override public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
if (newValue == null || "".equals(newValue) || (intField.minValue<0 && "-".equals(newValue))) {
value.setValue(0);
return;
}
final int intValue = Integer.parseInt(newValue);
if (intField.minValue > intValue || intValue > intField.maxValue) {
textProperty().setValue(oldValue);
}
value.set(Integer.parseInt(textProperty().get()));
}
});
}
}
ฉันรู้ว่านี่เป็นกระทู้ที่ค่อนข้างเก่า แต่สำหรับผู้อ่านในอนาคตนี่เป็นอีกวิธีหนึ่งที่ฉันพบว่าใช้งานง่าย:
public class NumberTextField extends TextField
{
@Override
public void replaceText(int start, int end, String text)
{
if (validate(text))
{
super.replaceText(start, end, text);
}
}
@Override
public void replaceSelection(String text)
{
if (validate(text))
{
super.replaceSelection(text);
}
}
private boolean validate(String text)
{
return text.matches("[0-9]*");
}
}
text.matches("\\d+");
และฉันไม่สามารถลบอักขระใด ๆ ในฟิลด์ข้อความได้
เริ่มต้นด้วย JavaFX 8u40 คุณสามารถตั้งค่าวัตถุ TextFormatter บนช่องข้อความ:
UnaryOperator<Change> filter = change -> {
String text = change.getText();
if (text.matches("[0-9]*")) {
return change;
}
return null;
};
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
fieldNport = new TextField();
fieldNport.setTextFormatter(textFormatter);
สิ่งนี้จะหลีกเลี่ยงทั้งการคลาสย่อยและเหตุการณ์การเปลี่ยนแปลงที่ซ้ำกันซึ่งคุณจะได้รับเมื่อคุณเพิ่มตัวฟังการเปลี่ยนแปลงลงในคุณสมบัติข้อความและแก้ไขข้อความในตัวฟังนั้น
TextInput
มีTextFormatter
ซึ่งสามารถนำมาใช้ในรูปแบบแปลงและ จำกัด ประเภทของข้อความที่สามารถป้อนข้อมูล
TextFormatter
มีตัวกรองที่สามารถใช้ในการปฏิเสธการใส่ เราจำเป็นต้องตั้งค่านี้เพื่อปฏิเสธสิ่งที่ไม่ใช่จำนวนเต็มที่ถูกต้อง นอกจากนี้ยังมีตัวแปลงที่เราต้องตั้งค่าเพื่อแปลงค่าสตริงเป็นค่าจำนวนเต็มซึ่งเราสามารถผูกได้ในภายหลัง
ให้สร้างตัวกรองที่ใช้ซ้ำได้:
public class IntegerFilter implements UnaryOperator<TextFormatter.Change> {
private final static Pattern DIGIT_PATTERN = Pattern.compile("\\d*");
@Override
public Change apply(TextFormatter.Change aT) {
return DIGIT_PATTERN.matcher(aT.getText()).matches() ? aT : null;
}
}
ตัวกรองสามารถทำสิ่งใดสิ่งหนึ่งในสามสิ่งโดยสามารถคืนค่าการเปลี่ยนแปลงที่ไม่ได้แก้ไขเพื่อยอมรับตามที่เป็นอยู่สามารถแก้ไขการเปลี่ยนแปลงในลักษณะที่เห็นว่าเหมาะสมหรือสามารถคืนค่าได้ null
ไปปฏิเสธการเปลี่ยนแปลงทั้งหมดด้วยกัน
เราจะใช้มาตรฐาน IntegerStringConverter
เป็นตัวแปลง
เรามี:
TextField textField = ...;
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(), // Standard converter form JavaFX
defaultValue,
new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);
textField.setTextFormatter(formatter);
หากคุณไม่ต้องการฟิลเตอร์ที่ใช้ซ้ำได้คุณสามารถทำซับเดียวแบบนี้แทนได้:
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(),
defaultValue,
c -> Pattern.matches("\\d*", c.getText()) ? c : null );
ฉันไม่ชอบข้อยกเว้นดังนั้นฉันจึงใช้matches
ฟังก์ชันจาก String-Class
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (newValue.matches("\\d*")) {
int value = Integer.parseInt(newValue);
} else {
text.setText(oldValue);
}
}
});
textField.positionCaret(textField.getLength());
if (newValue.matches("\\d*") && newValue.getText().length < 5)
หากคุณต้องการ จำกัด อินพุตเป็น 4 หลักในกรณีนี้
เริ่มจากJava SE 8u40สำหรับความต้องการดังกล่าวคุณสามารถใช้ " จำนวนเต็ม "Spinner
เพื่อให้สามารถเลือกจำนวนเต็มที่ถูกต้องได้อย่างปลอดภัยโดยใช้ปุ่มลูกศรขึ้น / ลูกศรลงของแป้นพิมพ์หรือปุ่มลูกศรขึ้น / ลงที่มีให้
นอกจากนี้คุณยังสามารถกำหนดนาทีที่สูงสุดและเริ่มต้นความคุ้มค่าที่จะ จำกัด ค่าได้รับอนุญาตและจำนวนเงินที่จะเพิ่มขึ้นหรือลดลงโดยต่อขั้นตอน
ตัวอย่างเช่น
// Creates an integer spinner with 1 as min, 10 as max and 2 as initial value
Spinner<Integer> spinner1 = new Spinner<>(1, 10, 2);
// Creates an integer spinner with 0 as min, 100 as max and 10 as initial
// value and 10 as amount to increment or decrement by, per step
Spinner<Integer> spinner2 = new Spinner<>(0, 100, 10, 10);
ตัวอย่างผลลัพธ์ที่มี " จำนวนเต็มปินเนอร์ " " และสปินเนอร์ " คู่ "
สปินเนอร์คือตัวควบคุมฟิลด์ข้อความบรรทัดเดียวที่ให้ผู้ใช้เลือกตัวเลขหรือค่าอ็อบเจ็กต์จากลำดับของค่าดังกล่าว โดยทั่วไปแล้ว Spinners จะมีปุ่มลูกศรเล็ก ๆ คู่หนึ่งสำหรับก้าวผ่านองค์ประกอบต่างๆของลำดับ แป้นลูกศรขึ้น / ลงของแป้นพิมพ์ยังวนรอบองค์ประกอบต่างๆ ผู้ใช้อาจได้รับอนุญาตให้พิมพ์ค่า (ถูกกฎหมาย) ลงในสปินเนอร์โดยตรง แม้ว่ากล่องคำสั่งผสมจะมีฟังก์ชันการทำงานที่คล้ายคลึงกัน แต่บางครั้งตัวหมุนก็เป็นที่ต้องการเนื่องจากไม่ต้องการรายการแบบเลื่อนลงที่สามารถปิดบังข้อมูลที่สำคัญได้และเนื่องจากอนุญาตให้ใช้คุณลักษณะต่างๆเช่นการตัดจากค่าสูงสุดกลับไปเป็นค่าต่ำสุด (เช่น จากจำนวนเต็มบวกมากที่สุดถึง 0)
รายละเอียดเพิ่มเติมเกี่ยวกับการควบคุม Spinner
คำตอบที่เตรียมไว้อาจมีขนาดเล็กลงหากคุณใช้ Java 1.8 Lambdas
textfield.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.matches("\\d*")) return;
textfield.setText(newValue.replaceAll("[^\\d]", ""));
});
TextField text = new TextField();
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try {
Integer.parseInt(newValue);
if (newValue.endsWith("f") || newValue.endsWith("d")) {
manualPriceInput.setText(newValue.substring(0, newValue.length()-1));
}
} catch (ParseException e) {
text.setText(oldValue);
}
}
});
if
ข้อเป็นสิ่งที่สำคัญต่อปัจจัยการผลิตเช่นจับ 0.5d หรือ 0.7f ซึ่งมีการแยกวิเคราะห์อย่างถูกต้องโดย Int.parseInt () แต่ไม่ควรจะปรากฏในช่องข้อความ
ลองใช้รหัสง่ายๆนี้มันจะทำงานได้
DecimalFormat format = new DecimalFormat( "#.0" );
TextField field = new TextField();
field.setTextFormatter( new TextFormatter<>(c ->
{
if ( c.getControlNewText().isEmpty() )
{
return c;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = format.parse( c.getControlNewText(), parsePosition );
if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
}));
หากคุณต้องการใช้ผู้ฟังเดียวกันกับ TextField มากกว่าหนึ่งรายการนี่คือวิธีที่ง่ายที่สุด:
TextField txtMinPrice, txtMaxPrice = new TextField();
ChangeListener<String> forceNumberListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*"))
((StringProperty) observable).set(oldValue);
};
txtMinPrice.textProperty().addListener(forceNumberListener);
txtMaxPrice.textProperty().addListener(forceNumberListener);
อันนี้ใช้ได้ผลสำหรับฉัน
public void RestrictNumbersOnly(TextField tf){
tf.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (!newValue.matches("|[-\\+]?|[-\\+]?\\d+\\.?|[-\\+]?\\d+\\.?\\d+")){
tf.setText(oldValue);
}
}
});
}
ฉันต้องการความช่วยเหลือจากการรวมคำตอบของ Evan Knowles TextFormatter
จาก JavaFX 8
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.getControlNewText().matches("\\d*"))
return null;
else
return c;
}
));
ขอให้โชคดี;) ใจเย็น ๆ และเขียนโค้ดจาวา
นี่คือคลาสง่ายๆที่จัดการการตรวจสอบความถูกต้องพื้นฐานTextField
โดยใช้TextFormatter
แนะนำใน JavaFX 8u40
แก้ไข:
(รหัสเพิ่มเกี่ยวกับความคิดเห็นของ Floern)
import java.text.DecimalFormatSymbols;
import java.util.regex.Pattern;
import javafx.beans.NamedArg;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
public class TextFieldValidator {
private static final String CURRENCY_SYMBOL = DecimalFormatSymbols.getInstance().getCurrencySymbol();
private static final char DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();
private final Pattern INPUT_PATTERN;
public TextFieldValidator(@NamedArg("modus") ValidationModus modus, @NamedArg("countOf") int countOf) {
this(modus.createPattern(countOf));
}
public TextFieldValidator(@NamedArg("regex") String regex) {
this(Pattern.compile(regex));
}
public TextFieldValidator(Pattern inputPattern) {
INPUT_PATTERN = inputPattern;
}
public static TextFieldValidator maxFractionDigits(int countOf) {
return new TextFieldValidator(maxFractionPattern(countOf));
}
public static TextFieldValidator maxIntegers(int countOf) {
return new TextFieldValidator(maxIntegerPattern(countOf));
}
public static TextFieldValidator integersOnly() {
return new TextFieldValidator(integersOnlyPattern());
}
public TextFormatter<Object> getFormatter() {
return new TextFormatter<>(this::validateChange);
}
private Change validateChange(Change c) {
if (validate(c.getControlNewText())) {
return c;
}
return null;
}
public boolean validate(String input) {
return INPUT_PATTERN.matcher(input).matches();
}
private static Pattern maxFractionPattern(int countOf) {
return Pattern.compile("\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?");
}
private static Pattern maxCurrencyFractionPattern(int countOf) {
return Pattern.compile("^\\" + CURRENCY_SYMBOL + "?\\s?\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?\\s?\\" +
CURRENCY_SYMBOL + "?");
}
private static Pattern maxIntegerPattern(int countOf) {
return Pattern.compile("\\d{0," + countOf + "}");
}
private static Pattern integersOnlyPattern() {
return Pattern.compile("\\d*");
}
public enum ValidationModus {
MAX_CURRENCY_FRACTION_DIGITS {
@Override
public Pattern createPattern(int countOf) {
return maxCurrencyFractionPattern(countOf);
}
},
MAX_FRACTION_DIGITS {
@Override
public Pattern createPattern(int countOf) {
return maxFractionPattern(countOf);
}
},
MAX_INTEGERS {
@Override
public Pattern createPattern(int countOf) {
return maxIntegerPattern(countOf);
}
},
INTEGERS_ONLY {
@Override
public Pattern createPattern(int countOf) {
return integersOnlyPattern();
}
};
public abstract Pattern createPattern(int countOf);
}
}
คุณสามารถใช้งานได้ดังนี้:
textField.setTextFormatter(new TextFieldValidator(ValidationModus.INTEGERS_ONLY).getFormatter());
หรือคุณสามารถสร้างอินสแตนซ์ได้ในไฟล์ fxml และนำไปใช้กับ customTextField ด้วยคุณสมบัติตาม
app.fxml:
<fx:define>
<TextFieldValidator fx:id="validator" modus="INTEGERS_ONLY"/>
</fx:define>
CustomTextField.class:
public class CustomTextField {
private TextField textField;
public CustomTextField(@NamedArg("validator") TextFieldValidator validator) {
this();
textField.setTextFormatter(validator.getFormatter());
}
}
นี่คือสิ่งที่ฉันใช้:
private TextField textField;
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
}
});
สิ่งเดียวกันในสัญกรณ์แลมบ์ดาจะเป็น:
private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
});
วิธีนี้ช่วยให้ TextField เสร็จสิ้นการประมวลผลทั้งหมด (คัดลอก / วาง / เลิกทำปลอดภัย) ไม่จำเป็นต้องขยายคลาสและช่วยให้คุณตัดสินใจว่าจะทำอย่างไรกับข้อความใหม่หลังจากการเปลี่ยนแปลงทุกครั้ง (เพื่อผลักดันให้เป็นตรรกะหรือเปลี่ยนกลับไปเป็นค่าก่อนหน้าหรือแม้กระทั่งแก้ไข)
// fired by every text property change
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
สำหรับกรณีของคุณเพียงแค่เพิ่มตรรกะนี้ภายใน ทำงานได้อย่างสมบูรณ์
if (newValue.equals("")) return;
try {
Integer i = Integer.valueOf(newValue);
// do what you want with this i
} catch (Exception e) {
((StringProperty)observable).setValue(oldValue);
}
อืมมม ฉันพบปัญหานั้นเมื่อหลายสัปดาห์ก่อน เนื่องจาก API ไม่ได้ให้การควบคุมเพื่อให้บรรลุเป้าหมายดังกล่าว
คุณอาจต้องการใช้API ของคุณเอง ฉันใช้สิ่งที่ชอบ:
public class IntegerBox extends TextBox {
public-init var value : Integer = 0;
protected function apply() {
try {
value = Integer.parseInt(text);
} catch (e : NumberFormatException) {}
text = "{value}";
}
override var focused = false on replace {apply()};
override var action = function () {apply()}
}
โดยจะใช้วิธีเดียวกับที่ปกติTextBox
,
แต่ยังมีvalue
แอตทริบิวต์ที่เก็บจำนวนเต็มป้อน
เมื่อตัวควบคุมสูญเสียโฟกัสตัวควบคุมจะตรวจสอบความถูกต้องของค่าและเปลี่ยนกลับ (ถ้าไม่ถูกต้อง)
รหัสนี้ทำให้ฟิลด์ข้อความของคุณยอมรับเฉพาะตัวเลข
textField.lengthProperty().addListener((observable, oldValue, newValue) -> {
if(newValue.intValue() > oldValue.intValue()){
char c = textField.getText().charAt(oldValue.intValue());
/** Check if the new character is the number or other's */
if( c > '9' || c < '0'){
/** if it's not number then just setText to previous one */
textField.setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
});
รหัสนี้ใช้ได้ดีสำหรับฉันแม้ว่าคุณจะพยายามคัดลอก / วาง
myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
myTextField.setText(oldValue);
}
});
ในการอัปเดตล่าสุดของ JavaFX คุณต้องตั้งค่าข้อความใหม่ในเมธอด Platform.runLater เช่นนี้:
private void set_normal_number(TextField textField, String oldValue, String newValue) {
try {
int p = textField.getCaretPosition();
if (!newValue.matches("\\d*")) {
Platform.runLater(() -> {
textField.setText(newValue.replaceAll("[^\\d]", ""));
textField.positionCaret(p);
});
}
} catch (Exception e) {
}
}
เป็นความคิดที่ดีที่จะตั้งตำแหน่งคาเร็ตด้วย
Platform.runLater
ได้ไหมว่าทำไมถึงจำเป็น?
ฉันต้องการปรับปรุงคำตอบของ Evan Knowles: https://stackoverflow.com/a/30796829/2628125
ในกรณีของฉันฉันมีคลาสที่มีตัวจัดการสำหรับส่วน UI Component การเริ่มต้น:
this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));
และวิธี numbericSanitization:
private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
final String allowedPattern = "\\d*";
if (!newValue.matches(allowedPattern)) {
this.dataText.setText(oldValue);
}
}
คีย์เวิร์ดซิงโครไนซ์เพื่อป้องกันปัญหาการล็อคการแสดงผลที่อาจเกิดขึ้นใน javafx หาก setText จะถูกเรียกก่อนที่จะดำเนินการเสร็จสิ้น เป็นเรื่องง่ายที่จะทำซ้ำหากคุณจะเริ่มพิมพ์ตัวอักษรผิดเร็วมาก
ข้อดีอีกอย่างคือคุณเก็บเพียงรูปแบบเดียวที่จะจับคู่และทำการย้อนกลับ จะดีกว่าเพราะคุณสามารถแยกสารละลายสำหรับรูปแบบการฆ่าเชื้อที่แตกต่างกันได้อย่างง่ายดาย
rate_text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
String s="";
for(char c : newValue.toCharArray()){
if(((int)c >= 48 && (int)c <= 57 || (int)c == 46)){
s+=c;
}
}
rate_text.setText(s);
}
});
ใช้งานได้ดีเนื่องจากอนุญาตให้คุณป้อนเฉพาะค่าจำนวนเต็มและค่าทศนิยม (มีรหัส ASCII 46)