Java 使用Scanner()读取CSV

Java 使用Scanner()读取CSV,java,csv,java.util.scanner,Java,Csv,Java.util.scanner,我的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; i

我的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 第一个,最后一个,电子邮件,地址 1、地址 2. 约翰,史密斯,blah@blah.com,123 圣。 街道, 简,史密斯,blech@blech.com,4455 罗杰 Cir,apt 2.
我是否错误地使用了扫描仪?

如果您必须使用扫描仪,则必须通过其
useDelimiter(…)
方法设置其分隔符。否则,它将默认使用所有空白作为分隔符。更好的方法是使用CSV库,因为这是他们最擅长的

例如,此分隔符将在逗号上拆分(带或不带空格):

scanner.useDelimiter("\\s*,\\s*");
请查看更多关于此的信息

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|
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库。首先,此(和
使用分隔符(“,”
解决方案)将无法正确处理包含
字符的引用标识符。

请停止编写错误的CSV解析器

我在网上看到了数百个CSV解析器和所谓的教程

几乎每个人都错了

这并不是一件坏事,因为它不会影响我,但那些试图编写CSV阅读器并出错的人也倾向于编写CSV阅读器。也会把他们弄错。这些我必须为之编写解析器

请记住CSV(按不明显程度增加的顺序):

  • 值周围可以有引号字符
  • 可以有除“”以外的其他引用字符
  • 甚至可以有除“and”以外的其他引用字符
  • 不能有任何引用字符
  • 甚至可以在某些值上有引号字符,而在其他值上没有引号字符
  • 可以有除、和以外的其他分离器
  • 分隔符和(引用的)值之间可以有空格
  • 可以有ascii以外的其他字符集
  • 应在每行中具有相同数量的值,但并不总是如此
  • 可以包含空字段,可以是带引号的:
    “foo”、“”、“bar”
    ,也可以不是:
    “foo”、“bar”
  • 可以在值中包含换行符
  • 如果值中没有分隔符,则不能包含新行
  • 值之间不能包含换行符
  • 如果正确转义,则可以在值内包含分隔符
  • 不使用反斜杠转义分隔符,但
  • 使用引用字符本身对其进行转义,例如,
    Frodo's Ring
    将是
    “Frodo's Ring”
  • 可以在值的开头或结尾使用引号字符,甚至可以作为唯一字符(
    “foo”“,“bar”“,”“
  • 甚至可以在not quoted值中包含带引号的字符;这一个没有逃脱
  • 如果你认为这显然不是问题,那么再想想。我看到这些项目中的每一项都执行错误。甚至在主要的软件包中。(如办公套房、CRM系统)

    有好的和正确的开箱即用CSV阅读器和写入器:


    如果您坚持自己编写代码,至少要阅读(非常简短)。

    好吧,我在NetBeans 8.1中编写代码:

    首先:创建一个新项目,选择Java应用程序并命名您的项目

    然后在public类之后修改代码,如下所示:

    /**
     * @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()+"|");
             }}
        }
    }
    
    按此分隔符拆分下一行():
    (?=([^\“]*\”[^\“]*\”*[^\“]*$)”

    我同意Scheintod的观点,即使用现有的CSV库从一开始就符合RFC-4180标准是一个好主意。除了提到的OpenCSV和Oster Miller之外,还有一系列其他CSV库。如果您对性能感兴趣,您可以查看。它表明

    始终是使用JDK 6、7、8或9最快的。这项研究没有发现这三者中的任何一个存在任何RFC 4180兼容性问题OpenCSV和Oster Miller的速度都是它们的两倍。

    我与作者没有任何关联,但关于uniVocity CSV解析器,由于其作者与该解析器的作者相同,该研究可能存在偏见


    需要注意的是,《SimpleFlatMapper》的作者也发表了一篇仅比较这三者的文章。

    我看到了许多由于代码不处理引号(“)、引号中的换行符和引号中的引号而导致的生产问题;例如:“他说”“这个”“应该被解析为:他说了”“这个”

    正如前面提到的,许多CSV解析示例只是读取一行,然后用分隔符分隔该行。这是相当不完整和有问题的

    对于我和那些可能更喜欢build verses购买(或使用其他人的代码并处理他们的依赖关系)的人来说,我开始使用经典的文本解析编程,这对我很有用:

    /**
     * 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][]);
    }
    

    使用合适的CSV库扫描程序默认分隔符是空白,这可能就是问题所在。我是否正确地认为它没有分隔换行符?即,它将“e\n1”和“5\nX”作为单个标记读取?可能更复杂
    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]