英文:
Why object of array from file has NumberFormatException in Java?
问题
以下是翻译好的内容:
我需要逐行读取一个 .txt 文件,并将每一行存储到一个数组中。每个数组有 3 个元素,empId、name 和 salary,它们是 SalariedEmployee 类的字段。我的代码出现了 NumberFormatException,我不知道哪一步出错了。任何提示或帮助将不胜感激。谢谢。请不要关闭我的问题,我是一个 Java 初学者。
异常信息:
异常线程 "main" java.lang.NumberFormatException:对于输入字符串:"Kelsey"
在 java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054) 处发生
在 java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110) 处发生
在 java.base/java.lang.Double.parseDouble(Double.java:549) 处发生
在 com.maoding.Hw3_Part2.main(Hw3_Part2.java:23) 处发生
.txt 文件:
1, Kelsey, 65000
2, Jake, 89000
3, Carlos, 105000
4, Clarence, 58000
5, Pacheco, 68000
6, Piotr, 120000
7, Adam, 47000
8, Yorst, 77000
9, Silas, 93000
10, Yapsiong, 85000
SalariedEmployee 类:
public class SalariedEmployee {
// 实例变量
public int empId;
public String name;
public double salary;
// 构造函数
public SalariedEmployee(int empId, String name, double salary) {
this.empId = empId;
this.name = name;
this.salary = salary;
}
// 获取方法
public int getEmpId() {
return empId;
}
public String getName(){
return name;
}
public double getSalary(){
return salary;
}
main 类:
public class Hw3_Part2 {
public static void main(String[] args) throws IOException {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("/Users/hw3_employees.txt"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",|\\s+");
for (int i = 0; i < words.length; i++) {
empId = Integer.parseInt(words[0]);
name = words[1];
salary = Double.parseDouble(words[2]);
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(words);
System.out.println(se);
}
}
}
}
英文:
I need to read a .txt file line by line and store each line to an array. Each array has 3 elements, empId, name, salary, which are fields of SalariedEmployee class. My code has a NumberFormatException, I have no idea which step goes wrong. Any hint or help would be appreciated. Thanks. Please don't close my question, I'm a beginner of Java.
Exception message:
Exception in thread "main" java.lang.NumberFormatException: For input string: "Kelsey"
at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.base/java.lang.Double.parseDouble(Double.java:549)
at com.maoding.Hw3_Part2.main(Hw3_Part2.java:23)
.txt fie:
1, Kelsey, 65000
2, Jake, 89000
3, Carlos, 105000
4, Clarence, 58000
5, Pacheco, 68000
6, Piotr, 120000
7, Adam, 47000
8, Yorst, 77000
9, Silas, 93000
10, Yapsiong, 85000
SalariedEmployee class:
public class SalariedEmployee {
// instance variable
public int empId;
public String name;
public double salary;
// constructor
public SalariedEmployee(int empId, String name, double salary) {
this.empId = empId;
this.name = name;
this.salary = salary;
}
// get methods
public int getEmpId() {
return empId;
}
public String getName(){
return name;
}
public double getSalary(){
return salary;
}
main class:
public class Hw3_Part2 {
public static void main(String[] args) throws IOException {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("/Users/hw3_employees.txt"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",|\\s+");
for (int i = 0; i < words.length; i++) {
empId = Integer.parseInt(words[0]);
name = words[1];
salary = Double.parseDouble(words[2]);
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(words);
System.out.println(se);
}
}
}
答案1
得分: 1
问题出在你的正则表达式。
(",|\\s+")
- 这里如果遇到逗号或者空格就会进行分割。竖线表示或的关系。所以,你的数组实际上大小超过了3。
你需要的是根据逗号和空格的组合进行分割。因此,你的正则表达式应该是 (",\\s+")
。它需要是逗号后面跟着一个空格。
英文:
The problem is with your regex.
(",|\\s+")
- Here you are splitting it if it either encounters a comma or a space. The pipe stands for OR. So, your array is actually greater than size 3.
What you need is to split it on a combination of both comma and space. So, your regex should be (",\\s+")
. It needs to be a comma followed by a space.
答案2
得分: 0
你的问题出在对方法 split()
的正则表达式调用上。
我不会直接给你答案,而是尝试解释调试的过程。每个程序员都需要学会如何调试自己的代码。你也可以参考如何调试小程序。
由于我不能通过这个平台的功能向你展示如何在你的集成开发环境(IDE)中使用调试器,我将呈现一个替代过程,即插入输出到标准输出(通常是计算机屏幕)的方法。
由于错误信息告诉你,你的程序试图将 Kelsey 解析为一个数字,这可能表明 words
数组不是你期望的。因此,你需要显示 words
的实际值。以下代码实现了这一点。
public static void main(String[] args) throws IOException {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("/Users/hw3_employees.txt"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",|\\s+");
System.out.println("words.length = " + words.length); // 添加了这一行
for (int i = 0; i < words.length; i++) {
System.out.printf("words[0] = ^%s^%n", words[0]); // 添加了这一行
empId = Integer.parseInt(words[0]);
name = words[1];
System.out.printf("words[1] = ^%s^%n", words[1]); // 添加了这一行。
salary = Double.parseDouble(words[2]);
System.out.printf("words[2] = ^%s^%n", words[2]); // 添加了这一行。
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(words);
System.out.println(se);
}
}
}
当运行上述代码时,我得到以下输出。
words.length = 5
words[0] = ^1^
words[1] = ^^
Exception in thread "main" java.lang.NumberFormatException: For input string: "Kelsey"
at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.base/java.lang.Double.parseDouble(Double.java:549)
- 首先,我看到
words
包含五个元素而不是三个。 - 我在要显示的字符串上添加了
^
符号,以便更容易看到值中是否有空白。因此,我看到words[1]
是一个空字符串。
在这个阶段,你应该意识到需要更改正则表达式。要知道要将它更改为什么内容,请查看 split()
方法的 javadoc。它有一个链接到解释 Java 正则表达式引擎如何工作的 java.util.regex.Pattern
类的链接。然后尝试进行实验。编写一个单独的程序,将来自文本文件的一行硬编码为字符串,尝试不同的正则表达式,直到找到正确的。
现在,我将告诉你解决问题的一种方法,即仅仅在逗号上进行 split,然后对每个“单词”进行 trim。
public static void main(String[] args) throws IOException {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("/Users/hw3_employees.txt"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",");
System.out.println("words.length = " + words.length);
for (int i = 0; i < words.length; i++) {
System.out.printf("words[0] = ^%s^%n", words[0]);
empId = Integer.parseInt(words[0].trim());
name = words[1];
System.out.printf("words[1] = ^%s^%n", words[1]);
salary = Double.parseDouble(words[2].trim());
System.out.printf("words[2] = ^%s^%n", words[2]);
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(words);
System.out.println(se);
}
}
}
现在的输出是。
words.length = 3
words[0] = ^1^
words[1] = ^ Kelsey^
words[2] = ^ 65000^
[Ljava.lang.String;@49c2faae
SalariedEmployee@20ad9418
...
现在你可以从代码中删除“调试”语句。
注意,如果你想在打印 SalariedEmployee
对象时得到一个“人类可读”的字符串,你需要重写 toString() 方法,但这是一个不同的问题。随时提出另一个问题
英文:
Your problem is the regular expression in the call to method split()
.
Rather than just post the answer, I will try to explain the process of debugging. Every programmer needs to learn how to debug his code. You can also refer to How to Debug Small Programs
Because I can't show you, via the capabilities of this site, how to use the debugger in your IDE, I will present an alternative process, namely inserting methods that print to standard output (which is usually the computer screen).
Since the error message is telling you that your program is trying to parse Kelsey as a number, that should indicate that maybe the words
array is not what you expect it to be. Hence you need to display the actual value of words
. This is what the below code does.
public static void main(String[] args) throws IOException {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("/Users/hw3_employees.txt"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",|\\s+");
System.out.println("words.length = " + words.length); // Added this line
for (int i = 0; i < words.length; i++) {
System.out.printf("words[0] = ^%s^%n", words[0]); // Added this line
empId = Integer.parseInt(words[0]);
name = words[1];
System.out.printf("words[1] = ^%s^%n", words[1]); // Added this line.
salary = Double.parseDouble(words[2]);
System.out.printf("words[2] = ^%s^%n", words[2]); // Added this line.
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(words);
System.out.println(se);
}
}
}
This is the output I get when running the above code.
words.length = 5
words[0] = ^1^
words[1] = ^^
Exception in thread "main" java.lang.NumberFormatException: For input string: "Kelsey"
at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.base/java.lang.Double.parseDouble(Double.java:549)
- First of all, I see that
words
contains five elements and not three. - I add the
^
symbol to the string that I want to display as it helps me see if there is whitespace in the value. Hence I see thatwords[1]
is an empty string.
At this stage, you should realize that you need to change the regular expression. In order to know what to change it to, look at the javadoc for method split()
. It has a link to class java.util.regex.Pattern
which explains how the java regular expression engine works. Then try experimenting. Write a separate program. Hard code a string that is one line from your text file and see what results you get for different regular expressions until you find the right one.
For now, I will tell you that one way to solve the problem is simply to split on the comma alone and then trim each "word".
public static void main(String[] args) throws IOException {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("/Users/hw3_employees.txt"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",");
System.out.println("words.length = " + words.length);
for (int i = 0; i < words.length; i++) {
System.out.printf("words[0] = ^%s^%n", words[0]);
empId = Integer.parseInt(words[0].trim());
name = words[1];
System.out.printf("words[1] = ^%s^%n", words[1]);
salary = Double.parseDouble(words[2].trim());
System.out.printf("words[2] = ^%s^%n", words[2]);
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(words);
System.out.println(se);
}
}
}
Now the output is.
words.length = 3
words[0] = ^1^
words[1] = ^ Kelsey^
words[2] = ^ 65000^
[Ljava.lang.String;@49c2faae
SalariedEmployee@20ad9418
words[0] = ^1^
words[1] = ^ Kelsey^
words[2] = ^ 65000^
[Ljava.lang.String;@49c2faae
SalariedEmployee@31cefde0
words[0] = ^1^
words[1] = ^ Kelsey^
words[2] = ^ 65000^
[Ljava.lang.String;@49c2faae
SalariedEmployee@439f5b3d
words.length = 3
words[0] = ^2^
words[1] = ^ Jake^
words[2] = ^ 89000^
[Ljava.lang.String;@1d56ce6a
SalariedEmployee@5197848c
words[0] = ^2^
words[1] = ^ Jake^
words[2] = ^ 89000^
[Ljava.lang.String;@1d56ce6a
SalariedEmployee@17f052a3
words[0] = ^2^
words[1] = ^ Jake^
words[2] = ^ 89000^
[Ljava.lang.String;@1d56ce6a
SalariedEmployee@2e0fa5d3
words.length = 3
words[0] = ^3^
words[1] = ^ Carlos^
words[2] = ^ 105000^
[Ljava.lang.String;@5010be6
SalariedEmployee@685f4c2e
words[0] = ^3^
words[1] = ^ Carlos^
words[2] = ^ 105000^
[Ljava.lang.String;@5010be6
SalariedEmployee@7daf6ecc
words[0] = ^3^
words[1] = ^ Carlos^
words[2] = ^ 105000^
[Ljava.lang.String;@5010be6
SalariedEmployee@2e5d6d97
words.length = 3
words[0] = ^4^
words[1] = ^ Clarence^
words[2] = ^ 58000^
[Ljava.lang.String;@238e0d81
SalariedEmployee@31221be2
words[0] = ^4^
words[1] = ^ Clarence^
words[2] = ^ 58000^
[Ljava.lang.String;@238e0d81
SalariedEmployee@377dca04
words[0] = ^4^
words[1] = ^ Clarence^
words[2] = ^ 58000^
[Ljava.lang.String;@238e0d81
SalariedEmployee@728938a9
words.length = 3
words[0] = ^5^
words[1] = ^ Pacheco^
words[2] = ^ 68000^
[Ljava.lang.String;@21b8d17c
SalariedEmployee@6433a2
words[0] = ^5^
words[1] = ^ Pacheco^
words[2] = ^ 68000^
[Ljava.lang.String;@21b8d17c
SalariedEmployee@5910e440
words[0] = ^5^
words[1] = ^ Pacheco^
words[2] = ^ 68000^
[Ljava.lang.String;@21b8d17c
SalariedEmployee@6267c3bb
words.length = 3
words[0] = ^6^
words[1] = ^ Piotr^
words[2] = ^ 120000^
[Ljava.lang.String;@533ddba
SalariedEmployee@246b179d
words[0] = ^6^
words[1] = ^ Piotr^
words[2] = ^ 120000^
[Ljava.lang.String;@533ddba
SalariedEmployee@7a07c5b4
words[0] = ^6^
words[1] = ^ Piotr^
words[2] = ^ 120000^
[Ljava.lang.String;@533ddba
SalariedEmployee@26a1ab54
words.length = 3
words[0] = ^7^
words[1] = ^ Adam^
words[2] = ^ 47000^
[Ljava.lang.String;@3d646c37
SalariedEmployee@41cf53f9
words[0] = ^7^
words[1] = ^ Adam^
words[2] = ^ 47000^
[Ljava.lang.String;@3d646c37
SalariedEmployee@5a10411
words[0] = ^7^
words[1] = ^ Adam^
words[2] = ^ 47000^
[Ljava.lang.String;@3d646c37
SalariedEmployee@2ef1e4fa
words.length = 3
words[0] = ^8^
words[1] = ^ Yorst^
words[2] = ^ 77000^
[Ljava.lang.String;@306a30c7
SalariedEmployee@b81eda8
words[0] = ^8^
words[1] = ^ Yorst^
words[2] = ^ 77000^
[Ljava.lang.String;@306a30c7
SalariedEmployee@68de145
words[0] = ^8^
words[1] = ^ Yorst^
words[2] = ^ 77000^
[Ljava.lang.String;@306a30c7
SalariedEmployee@27fa135a
words.length = 3
words[0] = ^9^
words[1] = ^ Silas^
words[2] = ^ 93000^
[Ljava.lang.String;@46f7f36a
SalariedEmployee@421faab1
words[0] = ^9^
words[1] = ^ Silas^
words[2] = ^ 93000^
[Ljava.lang.String;@46f7f36a
SalariedEmployee@2b71fc7e
words[0] = ^9^
words[1] = ^ Silas^
words[2] = ^ 93000^
[Ljava.lang.String;@46f7f36a
SalariedEmployee@5ce65a89
words.length = 3
words[0] = ^10^
words[1] = ^ Yapsiong^
words[2] = ^ 85000^
[Ljava.lang.String;@25f38edc
SalariedEmployee@1a86f2f1
words[0] = ^10^
words[1] = ^ Yapsiong^
words[2] = ^ 85000^
[Ljava.lang.String;@25f38edc
SalariedEmployee@3eb07fd3
words[0] = ^10^
words[1] = ^ Yapsiong^
words[2] = ^ 85000^
[Ljava.lang.String;@25f38edc
SalariedEmployee@506c589e
Now you can remove the "debug" statements from your code.
Note that if you want a "human readable" string when you print a SalariedEmployee
object, you need to override method toString(), but that would be a different question. Feel free to ask another question
答案3
得分: 0
错误是正则表达式模式为',|\s+'。它不匹配数组大小。
我已经修改了代码来更正这个问题,将其改为',\s+',并添加了额外的步骤,以便代码不会打印类的哈希码,而是打印字符串。此外,while循环中的for循环是不必要的。
以下是修改后的代码:
class SalariedEmployee {
// 实例变量
private int empId;
private String name;
private double salary;
// 构造方法
public SalariedEmployee(int empId, String name, double salary) {
this.empId = empId;
this.name = name;
this.salary = salary;
}
// 获取方法
public int getEmpId() {
int e = this.empId;
return e;
}
public String getName() {
String n = this.name;
return n;
}
public double getSalary() {
Double sal = this.salary;
return sal;
}
@Override
public String toString() {
String s = this.getEmpId() + "," + this.getName() + "," + this.getSalary();
return s;
}
}
public class Stacktest {
public static void main(String[] args) throws Exception {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("文件位置在这里"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",\\s+");
empId = Integer.parseInt(words[0]);
name = words[1];
salary = Double.parseDouble(words[2]);
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(Arrays.toString(words));
System.out.println(se.toString());
}
}
}
通常情况下,当您打印对象时,内部会调用toString()
方法。它返回类名,@,引用字符串和位置的哈希码
。为了解决这个问题,您需要重写现有的toString()
方法并发送实际的字符串值。
在重写toString()之前的输出
[Ljava.lang.String;@6a5fc7f7
stacktest.SalariedEmployee@3b6eb2ec
[Ljava.lang.String;@1e643faf
stacktest.SalariedEmployee@6e8dacdf
[Ljava.lang.String;@7a79be86
stacktest.SalariedEmployee@34ce8af7
[Ljava.lang.String;@b684286
stacktest.SalariedEmployee@880ec60
[Ljava.lang.String;@3f3afe78
stacktest.SalariedEmployee@7f63425a
[Ljava.lang.String;@36d64342
stacktest.SalariedEmployee@39ba5a14
[Ljava.lang.String;@511baa65
stacktest.SalariedEmployee@340f438e
[Ljava.lang.String;@30c7da1e
stacktest.SalariedEmployee@5b464ce8
[Ljava.lang.String;@57829d67
stacktest.SalariedEmployee@19dfb72a
[Ljava.lang.String;@17c68925
stacktest.SalariedEmployee@7e0ea639
在重写toString()之后的输出
[1, Kelsey, 65000]
1,Kelsey,65000.0
[2, Jake, 89000]
2,Jake,89000.0
[3, Carlos, 105000]
3,Carlos,105000.0
[4, Clarence, 58000]
4,Clarence,58000.0
[5, Pacheco, 68000]
5,Pacheco,68000.0
[6, Piotr, 120000]
6,Piotr,120000.0
[7, Adam, 47000]
7,Adam,47000.0
[8, Yorst, 77000]
8,Yorst,77000.0
[9, Silas, 93000]
9,Silas,93000.0
[10, Yapsiong, 85000]
10,Yapsiong,85000.0
最好的做法是将类变量设置为私有,并使用公共的getter和setter方法来访问这些类变量。(我之所以这样说,是因为您提到您是Java的初学者)。我在代码中为私有变量使用了公共的getter方法。
英文:
The error is the regex pattern,|\\s+
. It doesn't match the array size.
I have modified the code to correct this as ,\\s+
, and added additional steps so that the code does not print the hashcode of the class instead of the String. Also that for loop
is not necessary in the while loop.
Here is the modified code:
class SalariedEmployee {
// instance variable
private int empId;
private String name;
private double salary;
// constructor
public SalariedEmployee(int empId, String name, double salary) {
this.empId = empId;
this.name = name;
this.salary = salary;
}
// get methods
public int getEmpId() {
int e = this.empId
return e;
}
public String getName(){
String n = this.name;
return n;
}
public double getSalary(){
Double sal = this.salary;
return sal;
}
@Override
public String toString(){
String s = this.getEmpId()+","+this.getName()+","+this.getSalary();
return s;
}
}
public class Stacktest{
public static void main(String[] args) throws Exception {
int empId;
String name;
double salary;
String[] words;
Scanner fileInput = new Scanner(new File("File Location here"));
while (fileInput.hasNextLine()) {
words = fileInput.nextLine().split(",\\s+");
//System.out.println(Arrays.toString(words));
empId = Integer.parseInt(words[0]);
name = words[1].toString();
salary = Double.parseDouble(words[2]);
SalariedEmployee se = new SalariedEmployee(empId, name, salary);
System.out.println(Arrays.toString(words));
System.out.println(se.toString());
}
}
}
Generally, when you print objects, toString()
is invoked internally. It returns the classname, @ which refers to string and a hashcode of the location
. To overcome this, you need to override the existing toString()
method and send the actual String value.
Output before toString() overriding
[Ljava.lang.String;@6a5fc7f7
stacktest.SalariedEmployee@3b6eb2ec
[Ljava.lang.String;@1e643faf
stacktest.SalariedEmployee@6e8dacdf
[Ljava.lang.String;@7a79be86
stacktest.SalariedEmployee@34ce8af7
[Ljava.lang.String;@b684286
stacktest.SalariedEmployee@880ec60
[Ljava.lang.String;@3f3afe78
stacktest.SalariedEmployee@7f63425a
[Ljava.lang.String;@36d64342
stacktest.SalariedEmployee@39ba5a14
[Ljava.lang.String;@511baa65
stacktest.SalariedEmployee@340f438e
[Ljava.lang.String;@30c7da1e
stacktest.SalariedEmployee@5b464ce8
[Ljava.lang.String;@57829d67
stacktest.SalariedEmployee@19dfb72a
[Ljava.lang.String;@17c68925
stacktest.SalariedEmployee@7e0ea639
Output after toString() overriding
[1, Kelsey, 65000]
1,Kelsey,65000.0
[2, Jake, 89000]
2,Jake,89000.0
[3, Carlos, 105000]
3,Carlos,105000.0
[4, Clarence, 58000]
4,Clarence,58000.0
[5, Pacheco, 68000]
5,Pacheco,68000.0
[6, Piotr, 120000]
6,Piotr,120000.0
[7, Adam, 47000]
7,Adam,47000.0
[8, Yorst, 77000]
8,Yorst,77000.0
[9, Silas, 93000]
9,Silas,93000.0
[10, Yapsiong, 85000]
10,Yapsiong,85000.0
It is a best practice to set class variables to private and use public getter and setter methods to access those class variables.(I'm saying this because you mentioned you are a beginner in java). I used public getters for the private variables in the code.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论