我的csv正在读入System.out,但我注意到任何带空格的文本都会被移动到下一行(作为返回\ n)
以下是我的csv开始的方式:
first,last,email,address 1, address 2
john,smith,blah@blah.com,123 St. Street,
Jane,Smith,blech@blech.com,4455 Roger Cir,apt 2
运行我的应用程序后,任何带有空格(地址1)的单元格都会被抛到下一行。
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class main {
public static void main(String[] args) {
// -define .csv file in app
String fileNameDefined = "uploadedcsv/employees.csv";
// -File class needed to turn stringName to actual file
File file = new File(fileNameDefined);
try{
// -read from filePooped with Scanner class
Scanner inputStream = new Scanner(file);
// hashNext() loops line-by-line
while(inputStream.hasNext()){
//read single line, put in string
String data = inputStream.next();
System.out.println(data + "***");
}
// after loop, close scanner
inputStream.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
}
所以这是控制台中的结果:
first,last,email,address 1,address 2 john,smith,blah@blah.com,123 St. Street, Jane,Smith,blech@blech.com,4455 Roger Cir,apt 2
我是否错误地使用了扫描仪?
答案 0 :(得分:139)
请停止编写错误的CSV解析器!
我已经在网上看到了数百种CSV解析器和所谓的教程。
几乎每个人都弄错了!
这不会是一件坏事,因为它不会影响我,但尝试编写CSV 读者并弄错的人往往会写CSV 编写者也是。也让他们错了。而这些我必须为其编写解析器。
请记住CSV(按顺序增加不那么明显):
"foo","","bar"
或不:"foo",,"bar"
Frodo's Ring
将为'Frodo''s Ring'
"foo""", """bar", """"
)如果您认为这显然不是问题,那么请再想一想。我已经看到这些项目的每一个都被错误地执行了。即使在主要软件包中也是如此。 (例如Office-Suites,CRM Systems)
有很好的正确开箱即用的CSV读者和作者:
如果你坚持自己写作至少阅读(非常短)RFC for CSV。
答案 1 :(得分:39)
scanner.useDelimiter(",");
这应该有用。
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class TestScanner {
public static void main(String[] args) throws FileNotFoundException {
Scanner scanner = new Scanner(new File("/Users/pankaj/abc.csv"));
scanner.useDelimiter(",");
while(scanner.hasNext()){
System.out.print(scanner.next()+"|");
}
scanner.close();
}
}
对于CSV文件:
a,b,c d,e
1,2,3 4,5
X,Y,Z A,B
输出是:
a|b|c d|e
1|2|3 4|5
X|Y|Z A|B|
答案 2 :(得分:8)
Scanner.next()
不会读取换行符,而是读取由空格分隔的下一个标记(默认情况下,如果未使用useDelimiter()
更改分隔符模式)。要阅读一行,请使用Scanner.nextLine()
。
读完一行后,您可以使用String.split(",")
将行分隔为字段。这样可以识别不包含所需字段数的行。使用useDelimiter(",");
将忽略文件的基于行的结构(每行包含由逗号分隔的字段列表)。例如:
while (inputStream.hasNextLine())
{
String line = inputStream.nextLine();
String[] fields = line.split(",");
if (fields.length >= 4) // At least one address specified.
{
for (String field: fields) System.out.print(field + "|");
System.out.println();
}
else
{
System.err.println("Invalid record: " + line);
}
}
如前所述,建议使用CSV库。例如,此(和useDelimiter(",")
解决方案)将无法正确处理包含,
个字符的带引号的标识符。
答案 3 :(得分:1)
我同意Scheintod的观点,即使用现有的CSV库是一开始就具有RFC-4180兼容性的好主意。除了提到的OpenCSV和Oster Miller,还有一系列其他CSV库。如果您对性能感兴趣,可以看看uniVocity/csv-parsers-comparison。它表明了
始终是使用JDK 6、7、8或9最快的方法。该研究未发现这三个文件中的任何RFC 4180兼容性问题。 发现OpenCSV和Oster Miller的速度大约是它们的两倍。
我与作者没有任何关联,但是关于uniVocity CSV解析器,该研究可能因其作者与该解析器相同而有所偏颇。
请注意,SimpleFlatMapper的作者还发布了performance comparison,仅比较了这三个。
答案 4 :(得分:0)
通过此分隔符拆分nextLine():
(?=([^\"]*\"[^\"]*\")*[^\"]*$)")
。
答案 5 :(得分:0)
我已经看到许多由代码不处理引号(“),引号内的换行符和引号内的引号引起的生产问题;例如:“他说”“ this”“”应解析为:他说“ this “
就像前面提到的,许多CSV解析示例仅读取一行,然后用分隔符将其分解。这是相当不完整和有问题的。
对于我以及可能更喜欢构建诗句的人(或使用其他人的代码并处理其依赖项)进行购买,我开始使用经典的文本解析程序,并且对我有用:
/**
* Parse CSV data into an array of String arrays. It handles double quoted values.
* @param is input stream
* @param separator
* @param trimValues
* @param skipEmptyLines
* @return an array of String arrays
* @throws IOException
*/
public static String[][] parseCsvData(InputStream is, char separator, boolean trimValues, boolean skipEmptyLines)
throws IOException
{
ArrayList<String[]> data = new ArrayList<String[]>();
ArrayList<String> row = new ArrayList<String>();
StringBuffer value = new StringBuffer();
int ch = -1;
int prevCh = -1;
boolean inQuotedValue = false;
boolean quoteAtStart = false;
boolean rowIsEmpty = true;
boolean isEOF = false;
while (true)
{
prevCh = ch;
ch = (isEOF) ? -1 : is.read();
// Handle carriage return line feed
if (prevCh == '\r' && ch == '\n')
{
continue;
}
if (inQuotedValue)
{
if (ch == -1)
{
inQuotedValue = false;
isEOF = true;
}
else
{
value.append((char)ch);
if (ch == '"')
{
inQuotedValue = false;
}
}
}
else if (ch == separator || ch == '\r' || ch == '\n' || ch == -1)
{
// Add the value to the row
String s = value.toString();
if (quoteAtStart && s.endsWith("\""))
{
s = s.substring(1, s.length() - 1);
}
if (trimValues)
{
s = s.trim();
}
rowIsEmpty = (s.length() > 0) ? false : rowIsEmpty;
row.add(s);
value.setLength(0);
if (ch == '\r' || ch == '\n' || ch == -1)
{
// Add the row to the result
if (!skipEmptyLines || !rowIsEmpty)
{
data.add(row.toArray(new String[0]));
}
row.clear();
rowIsEmpty = true;
if (ch == -1)
{
break;
}
}
}
else if (prevCh == '"')
{
inQuotedValue = true;
}
else
{
if (ch == '"')
{
inQuotedValue = true;
quoteAtStart = (value.length() == 0) ? true : false;
}
value.append((char)ch);
}
}
return data.toArray(new String[0][]);
}
单元测试:
String[][] data = parseCsvData(new ByteArrayInputStream("foo,\"\",,\"bar\",\"\"\"music\"\"\",\"carriage\r\nreturn\",\"new\nline\"\r\nnext,line".getBytes()), ',', true, true);
for (int rowIdx = 0; rowIdx < data.length; rowIdx++)
{
System.out.println(Arrays.asList(data[rowIdx]));
}
生成输出:
[foo, , , bar, "music", carriage
return, new
line]
[next, line]
答案 6 :(得分:-1)
如果您绝对必须使用扫描仪,则必须通过其useDelimiter(...)
方法设置其分隔符。否则,它将默认使用所有空格作为其分隔符。尽管已经说过,但更好 - 使用CSV库,因为这是他们最擅长的。
例如,此分隔符将在逗号上分割,包含或不包含空格:
scanner.useDelimiter("\\s*,\\s*");
请查看java.util.Scanner API了解更多信息。
答案 7 :(得分:-2)
好吧,我在NetBeans 8.1中进行编码:
首先:创建一个新项目,选择Java应用程序并命名您的项目。
然后在公共类之后修改代码,如下所示:
/**
* @param args the command line arguments
* @throws java.io.FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException {
try (Scanner scanner = new Scanner(new File("C:\\Users\\YourName\\Folder\\file.csv"))) {
scanner.useDelimiter(",");
while(scanner.hasNext()){
System.out.print(scanner.next()+"|");
}}
}
}