การจัดการกับจุลภาคในไฟล์ CSV


472

ฉันกำลังมองหาคำแนะนำเกี่ยวกับวิธีจัดการไฟล์ csv ที่กำลังสร้างจากนั้นลูกค้าของเราอัปโหลดแล้วและอาจมีเครื่องหมายจุลภาคในค่าเช่นชื่อ บริษัท

บางส่วนของแนวคิดที่เรากำลังดูคือ: ตัวระบุที่ยกมา (ค่า "," ค่า "," ฯลฯ ) หรือใช้ | แทนเครื่องหมายจุลภาค ปัญหาที่ใหญ่ที่สุดคือเราต้องทำให้ง่ายหรือลูกค้าจะไม่ทำ


ลูกค้ากำลังเขียนและอัปโหลดมัน
Bob The Janitor

1
นี่คือวิธีการจัดการภายในคอมมาในไฟล์ csv เยี่ยมชมstackoverflow.com/questions/9889225/…
Hasan Abrar

บน iOS โดยพื้นฐานแล้วคุณต้องใช้ github.com/Flinesoft/CSVImporter
Fattie

3
โปรดทราบว่า QA นี้เก่า csvปัจจุบันหมายถึง RFC 4180และนั่นคือสิ่งที่
Fattie

ฉันมีปัญหาเดียวกันแน่นอนพยายามรวมคอลัมน์ในไฟล์ csv ซึ่งคั่นด้วยเครื่องหมายจุลภาค ไม่มีปัญหากับคำสั่ง awk น่าเสียดายที่บางเซลล์อาจมีเครื่องหมายจุลภาค (ในตัวอย่างเช่นที่อยู่) เซลล์อื่นจะไม่ กำลังมองหาโซลูชันที่เข้ากันได้กับ Linux แต่ไม่แน่ใจว่าจะเริ่มต้นอย่างไร
greenage

คำตอบ:


223

ดังที่คนอื่น ๆ พูดกันคุณต้องหลีกเลี่ยงค่าที่มีเครื่องหมายคำพูด นี่คือตัวอ่าน CSV เล็กน้อยในC♯ที่รองรับค่าที่ยกมารวมถึงเครื่องหมายคำพูดฝังตัวและการขึ้นบรรทัดใหม่

โดยวิธีการนี้เป็นรหัสทดสอบหน่วย ฉันโพสต์ตอนนี้เพราะคำถามนี้ดูเหมือนจะเกิดขึ้นมากมายและคนอื่น ๆ อาจไม่ต้องการห้องสมุดทั้งหมดเมื่อการสนับสนุน CSV ง่ายจะทำ

คุณสามารถใช้มันได้ดังต่อไปนี้:

using System;
public class test
{
    public static void Main()
    {
        using ( CsvReader reader = new CsvReader( "data.csv" ) )
        {
            foreach( string[] values in reader.RowEnumerator )
            {
                Console.WriteLine( "Row {0} has {1} values.", reader.RowIndex, values.Length );
            }
        }
        Console.ReadLine();
    }
}

นี่คือคลาส โปรดทราบว่าคุณสามารถใช้Csv.Escapeฟังก์ชันเพื่อเขียน CSV ที่ถูกต้องได้เช่นกัน

using System.IO;
using System.Text.RegularExpressions;

public sealed class CsvReader : System.IDisposable
{
    public CsvReader( string fileName ) : this( new FileStream( fileName, FileMode.Open, FileAccess.Read ) )
    {
    }

    public CsvReader( Stream stream )
    {
        __reader = new StreamReader( stream );
    }

    public System.Collections.IEnumerable RowEnumerator
    {
        get {
            if ( null == __reader )
                throw new System.ApplicationException( "I can't start reading without CSV input." );

            __rowno = 0;
            string sLine;
            string sNextLine;

            while ( null != ( sLine = __reader.ReadLine() ) )
            {
                while ( rexRunOnLine.IsMatch( sLine ) && null != ( sNextLine = __reader.ReadLine() ) )
                    sLine += "\n" + sNextLine;

                __rowno++;
                string[] values = rexCsvSplitter.Split( sLine );

                for ( int i = 0; i < values.Length; i++ )
                    values[i] = Csv.Unescape( values[i] );

                yield return values;
            }

            __reader.Close();
        }
    }

    public long RowIndex { get { return __rowno; } }

    public void Dispose()
    {
        if ( null != __reader ) __reader.Dispose();
    }

    //============================================


    private long __rowno = 0;
    private TextReader __reader;
    private static Regex rexCsvSplitter = new Regex( @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))" );
    private static Regex rexRunOnLine = new Regex( @"^[^""]*(?:""[^""]*""[^""]*)*""[^""]*$" );
}

public static class Csv
{
    public static string Escape( string s )
    {
        if ( s.Contains( QUOTE ) )
            s = s.Replace( QUOTE, ESCAPED_QUOTE );

        if ( s.IndexOfAny( CHARACTERS_THAT_MUST_BE_QUOTED ) > -1 )
            s = QUOTE + s + QUOTE;

        return s;
    }

    public static string Unescape( string s )
    {
        if ( s.StartsWith( QUOTE ) && s.EndsWith( QUOTE ) )
        {
            s = s.Substring( 1, s.Length - 2 );

            if ( s.Contains( ESCAPED_QUOTE ) )
                s = s.Replace( ESCAPED_QUOTE, QUOTE );
        }

        return s;
    }


    private const string QUOTE = "\"";
    private const string ESCAPED_QUOTE = "\"\"";
    private static char[] CHARACTERS_THAT_MUST_BE_QUOTED = { ',', '"', '\n' };
}

2
คุณอาจต้องแปล \ r \ n เพื่อให้สอดคล้องกับ windows ขึ้นอยู่กับแอปพลิเคชันของคุณ
Mandrake

3
@NadaNaeem สนใจที่จะทำอย่างละเอียด?
harpo

มันไม่นับรายการในแถวไฟล์ csv อย่างถูกต้อง แต่ก็ใช้งานได้ดีกับ
คอมม่า

-1 OP ไม่ได้ระบุภาษาที่สร้างไฟล์ หากโปรแกรมเมอร์คนอื่น ๆ มาที่นี่เพื่อหาทางออกในภาษาใด ๆ ยกเว้นภาษา C # พวกเขาจะไม่พบวิธีแก้ปัญหาที่พวกเขาสามารถใช้ในคำตอบนี้
Ben Leggiero

8
@ BenC.R.Leggiero ดังนั้นฉันคิดว่าคุณต้องลงคะแนนคำถามด้วยเพราะมาตรฐานของคุณไม่สามารถตอบได้ เนื่องจากมันเป็นรหัสที่ใช้ในการดำเนินการอย่างง่ายของสเปคอย่างง่ายและสามารถแปลเป็นภาษาที่ใช้กันทั่วไปได้อย่างง่ายดาย
harpo

395

สำหรับ 2017 มีการระบุ csv อย่างสมบูรณ์ - RFC 4180

มันเป็นข้อกำหนดที่พบบ่อยมากและถูกครอบคลุมโดยห้องสมุดหลายแห่ง ( ตัวอย่าง )

เพียงใช้ไลบรารี csv ใดก็ได้ที่มีให้ใช้ง่ายนั่นคือ RFC 4180


มีข้อมูลจำเพาะจริงสำหรับรูปแบบ CSV และวิธีจัดการเครื่องหมายจุลภาค:

ฟิลด์ที่มีตัวแบ่งบรรทัด (CRLF) เครื่องหมายคำพูดคู่และเครื่องหมายจุลภาคควรอยู่ในเครื่องหมายคำพูดคู่

http://tools.ietf.org/html/rfc4180

ดังนั้นเพื่อให้มีค่าfooและbar,bazคุณทำสิ่งนี้:

foo,"bar,baz"

ข้อกำหนดสำคัญอีกข้อที่ต้องพิจารณา (เช่นจากข้อกำหนด):

หากมีการใช้เครื่องหมายคำพูดคู่เพื่อใส่เขตข้อมูลดังนั้นเครื่องหมายอัญประกาศคู่ที่ปรากฏภายในเขตข้อมูลจะต้องถูกหลีกเลี่ยงโดยนำหน้าด้วยเครื่องหมายคำพูดคู่อื่น ตัวอย่างเช่น:

"aaa","b""bb","ccc"

120
"ฟิลด์ที่มีตัวแบ่งบรรทัด (CRLF), เครื่องหมายคำพูดคู่และเครื่องหมายจุลภาคควรอยู่ในเครื่องหมายคำพูดคู่"
อีไล

42
"หากใช้เครื่องหมายอัญประกาศคู่เพื่อใส่เขตข้อมูลดังนั้นเครื่องหมายอัญประกาศคู่ที่ปรากฏในเขตข้อมูลจะต้องถูกหลีกเลี่ยงโดยนำหน้าด้วยเครื่องหมายคำพูดคู่อื่น"
C. Dragon 76

11
ไม่ใช่สเป็คจริงๆ แต่ก็ยังมีประโยชน์ มันบอกว่า ... "ไม่มีข้อกำหนดที่เป็นทางการอยู่ซึ่งอนุญาตให้ตีความไฟล์ CSV ได้หลากหลายส่วนนี้เป็นเอกสารรูปแบบที่ดูเหมือนว่าจะตามมาด้วยการใช้งานส่วนใหญ่"
Justin Clarke

5
นอกจากนี้อย่าลืมว่าแม้จะมีชื่อค่า CSV ในแถวอาจคั่นด้วยเครื่องหมายจุลภาค - อย่างน้อยในแพลตฟอร์ม Windows มันขึ้นอยู่กับการตั้งค่าปัจจุบันในระดับภูมิภาค (intl.cpl ในบรรทัดคำสั่ง "การตั้งค่าขั้นสูง") System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatorโดยเฉพาะอย่างยิ่งแยกรายการ:
lxa

4
โปรดใส่ข้อมูลที่เกี่ยวข้องในคำตอบนี้นอกเหนือจากลิงก์ไปที่ A) ลบความคิดเห็นส่วนใหญ่ข้างต้น (และของฉัน), B) บันทึกผู้คนจำนวนมากยิ่งกว่าผู้ตอบเวลาที่จะไปที่หน้าอื่นและค้นหาที่เกี่ยวข้อง data, C) ป้องกัน Link Rot
user66001

76

รูปแบบ CSV ใช้เครื่องหมายจุลภาคเพื่อคั่นค่าที่มีการขึ้นบรรทัดใหม่, การป้อนบรรทัด, เครื่องหมายจุลภาคหรือเครื่องหมายคำพูดคู่ล้อมรอบด้วยเครื่องหมายคำพูดคู่ ค่าที่มีเครื่องหมายคำพูดคู่จะถูกยกมาและเครื่องหมายคำพูดแต่ละตัวจะถูกหลบหนีโดยคำพูดก่อนหน้านี้ทันที: ตัวอย่างเช่น 3 ค่า:

test
list, of, items
"go" he said

จะถูกเข้ารหัสเป็น:

test
"list, of, items"
"""go"" he said"

ข้อมูลใด ๆ ที่สามารถอ้าง แต่เพียงสาขาที่มีเครื่องหมายจุลภาค CR / NL หรือคำพูดที่จะต้องได้รับการยก

ไม่มีจริงเป็นมาตรฐานสำหรับรูปแบบ CSV แต่เกือบทุกการใช้งานตามแบบแผนเอกสารที่นี่ RFC ที่กล่าวถึงที่อื่นไม่ใช่มาตรฐานสำหรับ CSV เป็น RFC สำหรับการใช้ CSV ภายใน MIME และมีข้อ จำกัด ที่ไม่เป็นทางการและไม่จำเป็นซึ่งทำให้ไร้ประโยชน์นอก MIME

gotcha ที่โมดูล CSV จำนวนมากที่ฉันเห็นไม่รองรับคือข้อเท็จจริงที่ว่าหลายบรรทัดสามารถเข้ารหัสในฟิลด์เดียวซึ่งหมายความว่าคุณไม่สามารถสรุปได้ว่าแต่ละบรรทัดเป็นเร็กคอร์ดที่แยกจากกันคุณต้องไม่อนุญาตให้ขึ้นบรรทัดใหม่ในของคุณ ข้อมูลหรือเตรียมที่จะจัดการกับสิ่งนี้


40

ใส่เครื่องหมายคำพูดคู่ล้อมรอบสตริง นั่นคือโดยทั่วไปสิ่งที่ Excel ไม่

Ala Eli

คุณหลีกเลี่ยงการอ้างสองครั้งเป็นสองคำพูดคู่ เช่น "test1", "foo" "bar", "test2"


โดยพื้นฐานแล้วแนวคิดเดียวกันกับตัวระบุที่อ้างถึง
Bob The Janitor

1
คุณหลีกเลี่ยงคำพูดสองครั้งเป็นสองคำพูดคู่ เช่น "test1", "foo" "bar", "test2"
อีไล

เพียงแค่ใส่เครื่องหมายคำพูดคู่รอบสตริงจะไม่ทำงานเมื่อเครื่องหมายจุลภาค "ถูกตามด้วยทันที
MondKin

9

คุณสามารถใส่เครื่องหมายคำพูดคู่รอบ ๆ ฟิลด์ ฉันไม่ชอบวิธีการนี้เนื่องจากมันเพิ่มอักขระพิเศษอีกตัว (เครื่องหมายคำพูดคู่) เพียงกำหนดอักขระเลี่ยง (โดยปกติแล้วเป็นแบ็กสแลช) และใช้ทุกที่ที่คุณต้องการเพื่อหลบหนีบางสิ่ง:

ข้อมูล, ข้อมูลมากขึ้น, ข้อมูลเพิ่มเติม \, ยิ่งขึ้น, ยิ่งมากยิ่งขึ้น

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


3
รวดเร็วและสกปรก แต่ไม่ทำงานหากคุณมีรายการที่มี "\," จริงๆ
Sarp Kaya

1
Sarp นั่นเป็นสาเหตุที่ double \\ เป็นแบ็กสแลชที่รอดแล้วเนื่องจากตอนนี้กลายเป็นอักขระพิเศษอีกตัว
Grungondola

1
ใช้งานได้ แต่ไม่ใช่ CSV มันDSV
TRiG

8

มีไลบรารีที่พร้อมใช้งานผ่านทาง nuget สำหรับจัดการกับ CSV ที่มีรูปแบบที่ดีใด ๆ (.net) - CsvHelper

ตัวอย่างการแม็พกับคลาส:

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>();

ตัวอย่างการอ่านแต่ละฟิลด์:

var csv = new CsvReader( textReader );
while( csv.Read() )
{
    var intField = csv.GetField<int>( 0 );
    var stringField = csv.GetField<string>( 1 );
    var boolField = csv.GetField<bool>( "HeaderName" );
}

การปล่อยให้ไคลเอ็นต์ไดรฟ์รูปแบบไฟล์:
,เป็นตัวคั่นฟิลด์มาตรฐาน"เป็นค่ามาตรฐานที่ใช้เพื่อหนีฟิลด์ที่มีตัวคั่นอัญประกาศหรือสิ้นสุดบรรทัด

วิธีใช้ (ตัวอย่าง) #สำหรับฟิลด์และ'สำหรับการหลบหนี:

var csv = new CsvReader( textReader );
csv.Configuration.Delimiter = "#";
csv.Configuration.Quote = ''';
// read the file however meets your needs

เอกสารเพิ่มเติม


3
มันจะดีกว่าถ้าคุณรวมตัวอย่างของวิธีการใช้CsvHelperไลบรารีเพื่อแก้ปัญหาของ OP
George Stocker

ทำไมเกือบทุกอย่างใน. NET จึงต้องเป็น "ผู้ช่วยเหลือ" ... คำนี้อยู่ใกล้ความหมาย ... เหมือน "ผู้จัดการ"
bytedev

5

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

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

กล่าวคือ regex ควรเป็นสิ่งนี้โดยใช้ escapes ด้วยตนเอง:

",(?=(?:[^\"\"]*\"\"[^\"\"]*\"\")*(?![^\"\"]*\"\"))"

ซึ่งแปลเป็น ",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"

เมื่อใช้สตริง Vertabim @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"มันจะทำงานดังต่อไปนี้ตามที่คุณเห็นว่าคุณทำการดีบั๊กของ regex หรือไม่:

",(?=(?:[^"]*"[^"]*")*(?![^"]*"))"

โดยสรุปแล้วฉันขอแนะนำวิธีแก้ปัญหาของ harpo แต่ระวัง gotcha เล็กน้อยนี้!

ฉันได้รวมไว้ใน CsvReader แล้วเป็นทางเลือกที่ไม่ปลอดภัยเล็กน้อยที่จะแจ้งให้คุณทราบหากข้อผิดพลาดนี้เกิดขึ้น (หากคุณมีจำนวนคอลัมน์ที่ทราบล่วงหน้า):

if (_expectedDataLength > 0 && values.Length != _expectedDataLength) 
throw new DataLengthException(string.Format("Expected {0} columns when splitting csv, got {1}", _expectedDataLength, values.Length));

สิ่งนี้สามารถฉีดผ่านทางนวกรรมิก:

public CsvReader(string fileName, int expectedDataLength = 0) : this(new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
    _expectedDataLength = expectedDataLength;
}

คุณจะจัดการกับแถวส่วนหัวอย่างไร? ฉันพยายามที่จะแม CSV ไปยัง C # วัตถุที่มีทุกประเภท แต่แบ่งส่วนหัวแถวเพราะทุกสายของ ...
tCoe

ไม่[^""]เหมือนกัน[^"]ใช่ไหม การทำสำเนาอักขระภายในสเปคคลาสอักขระซ้ำซ้อนใช่มั้ย
Minh Tran

4

เพิ่มการอ้างอิงถึง Microsoft.VisualBasic (ใช่มันบอกว่า VisualBasic แต่ใช้งานได้ใน C # เช่นกัน - จำไว้ว่าท้ายที่สุดมันก็แค่ IL)

ใช้Microsoft.VisualBasic.FileIO.TextFieldParserคลาสเพื่อแยกวิเคราะห์ไฟล์ CSV นี่คือตัวอย่างรหัส:

 Dim parser As TextFieldParser = New TextFieldParser("C:\mar0112.csv")
 parser.TextFieldType = FieldType.Delimited
 parser.SetDelimiters(",")      

   While Not parser.EndOfData         
      'Processing row             
      Dim fields() As String = parser.ReadFields         
      For Each field As String In fields             
         'TODO: Process field                   

      Next      
      parser.Close()
   End While 

ใช่นี่เป็นคลาสที่มีประโยชน์มากใน namespace ที่ค่อนข้างโชคร้าย ;-) อย่างไรก็ตามเพื่อที่จะตอบคำถามเดิมคุณควรตั้งค่าparser.HasFieldsEnclosedInQuotes = true;และไฟล์อินพุตจะต้องใส่ฟิลด์ที่มีเครื่องหมายจุลภาคในเครื่องหมายคำพูดตามข้อมูลจำเพาะ CSV - excel ทำสิ่งนี้แล้ว
Christopher King

4

คุณสามารถใช้ "ตัวคั่น" ทางเลือกเช่น ";" หรือ "|" แต่ง่ายที่สุดอาจเป็นเพียงการอ้างอิงซึ่งได้รับการสนับสนุนโดยห้องสมุด CSV ที่ดีที่สุด

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับตัวคั่น CSV และข้อมูลจำเพาะสำหรับรูปแบบมาตรฐานสำหรับการอธิบายตัวคั่นและการอ้างอิงดูหน้าเว็บนี้


4

ในกรณีที่คุณอยู่ใน* ระวังระบบมีการเข้าถึงsedและสามารถมีได้หนึ่งหรือมากกว่าหนึ่งที่ไม่พึงประสงค์จุลภาคเฉพาะในสาขาเฉพาะของ CSV คุณสามารถใช้ต่อไปนี้หนึ่งซับในเพื่อที่จะใส่พวกเขาใน"ขณะที่RFC4180 มาตรา 2ข้อเสนอ:

sed -r 's/([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*)/\1"\2"\3/' inputfile

เครื่องหมายจุลภาคที่ไม่พึงประสงค์อาจอยู่ในฟิลด์ที่คุณต้องเปลี่ยน / ขยายกลุ่มการจับภาพของ regex (และการแทนที่) ขึ้นอยู่กับฟิลด์
ตัวอย่างข้างต้นจะล้อมรอบฟิลด์ที่สี่ (จากหก) ในเครื่องหมายคำพูด

ป้อนคำอธิบายรูปภาพที่นี่

เมื่อใช้ร่วมกับ--in-place-optionคุณสามารถนำการเปลี่ยนแปลงเหล่านี้ไปใช้กับไฟล์ได้โดยตรง

เพื่อที่จะ "สร้าง" regex ที่ถูกต้องมีหลักการง่ายๆที่จะปฏิบัติตาม:

  1. สำหรับทุกฟิลด์ใน CSV ของคุณที่มาก่อนฟิลด์ด้วยเครื่องหมายจุลภาค (s) ที่คุณไม่ต้องการให้คุณเขียนมัน[^,]*,และรวมไว้ในกลุ่มการจับภาพ
  2. สำหรับเขตที่มีเครื่องหมายจุลภาคที่ไม่พึงประสงค์ (s) (.*)ที่คุณเขียน
  3. สำหรับทุกฟิลด์หลังจากฟิลด์ด้วยเครื่องหมายจุลภาค (s) ที่คุณไม่ต้องการคุณเขียนมัน,.* และรวมทั้งหมดไว้ในกลุ่มการจับภาพ

นี่คือภาพรวมโดยย่อของ regexes / การทดแทนที่เป็นไปได้ที่แตกต่างกันขึ้นอยู่กับฟิลด์ที่ระบุ \1"\2"\3หากไม่ได้รับการทดแทนคือ

([^,]*)(,.*)                     #first field, regex
"\1"\2                           #first field, substitution

(.*,)([^,]*)                     #last field, regex
\1"\2"                           #last field, substitution


([^,]*,)(.*)(,.*,.*,.*)          #second field (out of five fields)
([^,]*,[^,]*,)(.*)(,.*)          #third field (out of four fields)
([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*) #fourth field (out of six fields)

หากคุณต้องการลบเครื่องหมายจุลภาคที่ไม่ต้องการด้วยsedแทนที่จะใส่เครื่องหมายคำพูดอ้างอิงคำตอบนี้


3

หากคุณรู้สึกอยากเปลี่ยนล้อใหม่สิ่งต่อไปนี้อาจใช้ได้กับคุณ:

public static IEnumerable<string> SplitCSV(string line)
{
    var s = new StringBuilder();
    bool escaped = false, inQuotes = false;
    foreach (char c in line)
    {
        if (c == ',' && !inQuotes)
        {
            yield return s.ToString();
            s.Clear();
        }
        else if (c == '\\' && !escaped)
        {
            escaped = true;
        }
        else if (c == '"' && !escaped)
        {
            inQuotes = !inQuotes;
        }
        else
        {
            escaped = false;
            s.Append(c);
        }
    }
    yield return s.ToString();
}

3

ในยุโรปเรามีปัญหานี้ต้องเร็วกว่าคำถามนี้ ในยุโรปเราใช้เครื่องหมายจุลภาคทั้งหมดสำหรับจุดทศนิยม ดูหมายเลขนี้ด้านล่าง:

| American      | Europe        |
| ------------- | ------------- |
| 0.5           | 0,5           |
| 3.14159265359 | 3,14159265359 |
| 17.54         | 17,54         |
| 175,186.15    | 175.186,15    |

ดังนั้นจึงไม่สามารถใช้ตัวคั่นจุลภาคสำหรับไฟล์ CSV เพราะเหตุผลที่ไฟล์ CSV ในยุโรปจะถูกคั่นด้วยเครื่องหมายอัฒภาค(; )

โปรแกรมเช่น Microsoft Excel สามารถอ่านไฟล์ที่มีเครื่องหมายอัฒภาคและเป็นไปได้ที่จะสลับจากตัวคั่น คุณสามารถใช้แท็บ ( \t) เป็นตัวคั่นได้ ดูคำตอบนี้จาก Supper ผู้ใช้


2

หากคุณสนใจในแบบฝึกหัดทางการศึกษาเพิ่มเติมเกี่ยวกับวิธีแยกวิเคราะห์ไฟล์โดยทั่วไป (ใช้ CSV เป็นตัวอย่าง) คุณสามารถดูบทความนี้โดย Julian Bucknall ฉันชอบบทความเพราะแบ่งสิ่งต่าง ๆ ออกเป็นปัญหาเล็ก ๆ ที่ไม่สามารถเอาชนะได้ คุณสร้างไวยากรณ์ก่อนและเมื่อคุณมีไวยากรณ์ที่ดีเป็นกระบวนการที่ค่อนข้างง่ายและมีระเบียบวิธีในการแปลงไวยากรณ์เป็นโค้ด

บทความใช้ C # และมีลิงค์ที่ด้านล่างเพื่อดาวน์โหลดรหัส


1

นี่เป็นวิธีแก้ปัญหาเล็กน้อยที่เรียบร้อย:

คุณสามารถใช้เครื่องหมายตัวเลขกรีกล่างแทนได้ (U + 0375)

ดูเหมือนว่านี้͵

การใช้วิธีนี้ช่วยให้คุณประหยัดทรัพยากรได้มากเช่นกัน ...


1

เพียงใช้SoftCircuits.CsvParserบน NuGet มันจะจัดการรายละเอียดทั้งหมดสำหรับคุณและจัดการไฟล์ที่มีขนาดใหญ่มากได้อย่างมีประสิทธิภาพ และหากจำเป็นมันยังสามารถนำเข้า / ส่งออกวัตถุโดยการแมปคอลัมน์กับคุณสมบัติของวัตถุ นอกจากนี้การทดสอบของฉันพบว่าค่าเฉลี่ยเร็วกว่า CsvHelper ยอดนิยมเกือบ 4 เท่า


0

นี่เป็นเรื่องเกี่ยวกับหลักปฏิบัติทั่วไปเริ่มจากกฎของหัวแม่มือ:

  1. อย่าใช้ CSV ใช้ XML กับห้องสมุดเพื่ออ่านและเขียนไฟล์ xml แทน

  2. หากคุณต้องใช้ CSV ทำอย่างถูกต้องและใช้ห้องสมุดฟรีเพื่อแยกและจัดเก็บไฟล์ CSV

หากต้องการแสดงให้เห็นว่า 1) ตัวแยกวิเคราะห์ CSV ส่วนใหญ่ไม่ได้เข้ารหัสให้ทราบดังนั้นหากคุณไม่ได้ติดต่อกับ US-ASCII คุณกำลังถามปัญหา ตัวอย่างเช่น excel 2002 กำลังจัดเก็บ CSV ในการเข้ารหัสในเครื่องโดยไม่ต้องมีหมายเหตุเกี่ยวกับการเข้ารหัส มาตรฐาน CSV ไม่ได้นำมาใช้กันอย่างแพร่หลาย :( ในทางตรงกันข้ามมาตรฐาน xml ได้รับการยอมรับอย่างดีและจัดการการเข้ารหัสได้ค่อนข้างดี

เพื่อแสดงให้เห็นถึง 2) มีตัวแยกวิเคราะห์ csv หลายภาษาสำหรับเกือบทุกภาษาดังนั้นจึงไม่จำเป็นต้องบูรณาการล้อใหม่แม้ว่าโซลูชันจะดูเรียบง่าย

เพื่อชื่อไม่กี่:

  • สำหรับหลามใช้ build ในโมดูลcsv

  • สำหรับ perl ตรวจสอบ CPAN และข้อความ :: CSV

  • สำหรับ php use build ใน fgetcsv / fputcsv

  • สำหรับการตรวจสอบ java ไลบรารีSuperCVS

จริงๆแล้วคุณไม่จำเป็นต้องดำเนินการนี้ด้วยตนเองหากคุณไม่ต้องการแยกวิเคราะห์บนอุปกรณ์ฝังตัว


12
XML ไม่ใช่คำตอบเสมอไป CSV เป็นรูปแบบที่ถูกต้องสำหรับงานเมื่อคุณมีข้อมูลหนาแน่นแบบตาราง (เช่นสเปรดชีต) แท็กเหล่านั้นแนะนำค่าใช้จ่ายจำนวนมากและหากแต่ละบรรทัดมีรูปแบบเหมือนกันไม่จำเป็นต้องมีความชัดเจนเกี่ยวกับความหมายของแต่ละค่าและทุกค่า XML นั้นยอดเยี่ยมเมื่อคุณมีข้อมูลลำดับชั้นที่ซับซ้อนหรือบันทึกด้วยฟิลด์ที่เป็นทางเลือก ไม่เป็นเช่นนั้นเสมอไป
Adam Jaskiewicz

ในทางทฤษฎี "แท็ก" แนะนำค่าใช้จ่ายเล็กน้อย แต่ฉันไม่สามารถนึกถึงการใช้งานจริงใด ๆ ที่มันเริ่มเป็นปัญหา คุณมีตัวอย่างที่ใช้ได้จริงหรือไม่? ในการทำงานกับข้อมูลเราควรใช้ฐานข้อมูลแทน csv ถ้าเราพูดเกี่ยวกับการจัดลำดับข้อมูล (การสำรองข้อมูลการแลกเปลี่ยนข้อมูล) จะสำคัญหรือไม่หากการแยกวิเคราะห์ใช้เวลาหนึ่งสัปดาห์แทนที่จะเป็น 5 วัน
Piotr Czapla

2
โดยทั่วไปสถานการณ์ใด ๆ ที่คุณมีข้อมูลที่แสดงโดยตารางได้ดีที่สุด สมมติว่าคุณมีข้อมูลจากเซ็นเซอร์ที่แตกต่างกันจำนวนโหลซึ่งคุณสุ่มตัวอย่างทุก ๆ ครั้งและคุณบันทึกการประทับเวลาและค่าของเซ็นเซอร์แต่ละตัวในเวลานั้น แต่ละระเบียนจะเหมือนกัน: การประทับเวลา, เซ็นเซอร์ 0, เซ็นเซอร์ 1, ... sensor11 XML นั้นยอดเยี่ยมสำหรับการแสดงข้อมูลที่ซับซ้อนและผิดปกติ แต่เป็นรูปแบบที่ค่อนข้างหนาซึ่งไม่เหมาะกับทุกสถานการณ์ KISS
Adam Jaskiewicz

10
บางคนเห็นปัญหาและพวกเขาพูดว่า "ฉันรู้ว่าฉันจะใช้ XML!" ตอนนี้พวกเขามีสองปัญหา
Adam Jaskiewicz

ฉันเห็นด้วยอย่างยิ่งว่า xml ไม่ใช่คำตอบสำหรับทุกสิ่ง โดยเฉพาะอย่างยิ่งมันไม่เหมาะกับการเปลี่ยนฐานข้อมูลหรือไฟล์การกำหนดค่า แต่ที่นี่คำถามเกี่ยวกับการแลกเปลี่ยนข้อมูลซึ่ง XML ได้รับการออกแบบ
Piotr Czapla

0

คุณสามารถอ่านไฟล์ csv เช่นนี้

สิ่งนี้ใช้ประโยชน์จากการแยกและดูแลช่องว่าง

ArrayList List = new ArrayList();
static ServerSocket Server;
static Socket socket;
static ArrayList<Object> list = new ArrayList<Object>();


public static void ReadFromXcel() throws FileNotFoundException
{   
    File f = new File("Book.csv");
    Scanner in = new Scanner(f);
    int count  =0;
    String[] date;
    String[] name;
    String[] Temp = new String[10];
    String[] Temp2 = new String[10];
    String[] numbers;
    ArrayList<String[]> List = new ArrayList<String[]>();
    HashMap m = new HashMap();

         in.nextLine();
         date = in.nextLine().split(",");
         name = in.nextLine().split(",");
         numbers = in.nextLine().split(",");
         while(in.hasNext())
         {
             String[] one = in.nextLine().split(",");
             List.add(one);
         }
         int xount = 0;
         //Making sure the lines don't start with a blank
         for(int y = 0; y<= date.length-1; y++)
         {
             if(!date[y].equals(""))
             {   
                 Temp[xount] = date[y];
                 Temp2[xount] = name[y];
                 xount++;
             }
         }

         date = Temp;
         name =Temp2;
         int counter = 0;
         while(counter < List.size())
         {
             String[] list = List.get(counter);
             String sNo = list[0];
             String Surname = list[1];
             String Name = list[2];
             for(int x = 3; x < list.length; x++)
             {           
                 m.put(numbers[x], list[x]);
             }
            Object newOne = new newOne(sNo, Name, Surname, m, false);
             StudentList.add(s);
             System.out.println(s.sNo);
             counter++;
         }

0

ก่อนอื่นเรามาถามตัวเองว่า "ทำไมเราถึงรู้สึกว่าจำเป็นต้องจัดการกับเครื่องหมายจุลภาคสำหรับไฟล์ CSV ต่างกัน"

สำหรับฉันคำตอบคือ "เพราะเมื่อฉันส่งออกข้อมูลไปยังไฟล์ CSV เครื่องหมายจุลภาคในฟิลด์จะหายไปและฟิลด์ของฉันจะถูกแยกออกเป็นหลายช่องซึ่งมีเครื่องหมายจุลภาคปรากฏอยู่ในข้อมูลต้นฉบับ" (นั่นเป็นเพราะเครื่องหมายจุลภาคเป็นตัวคั่นฟิลด์ CSV)

Semi colons อาจใช้เป็นตัวคั่นฟิลด์ CSV ทั้งนี้ขึ้นอยู่กับสถานการณ์ของคุณ

ด้วยความต้องการของฉันฉันสามารถใช้อักขระเช่นเครื่องหมายอัญประกาศเดี่ยวต่ำ 9 ซึ่งดูเหมือนคอมม่า

ดังนั้นนี่คือวิธีที่คุณสามารถทำได้ใน Go:

// Replace special CSV characters with single low-9 quotation mark
func Scrub(a interface{}) string {
    s := fmt.Sprint(a)
    s = strings.Replace(s, ",", "‚", -1)
    s = strings.Replace(s, ";", "‚", -1)
    return s
}

อักขระการค้นหาจุลภาคตัวที่สองในฟังก์ชันแทนที่คือทศนิยม 8218

โปรดระวังว่าหากคุณมีไคลเอนต์ที่อาจมีตัวอ่านข้อความ ASCII เท่านั้นว่าตัวละคร Decima 8218 นี้จะไม่เหมือนกับเครื่องหมายจุลภาค หากเป็นกรณีของคุณฉันแนะนำให้ใช้เครื่องหมายจุลภาค (หรือเซมิโคลอน) พร้อมเครื่องหมายคำพูดคู่ต่อ RFC 4128: https://tools.ietf.org/html/rfc4180


0

โดยทั่วไปฉันเข้ารหัส URL ฟิลด์ที่สามารถมีเครื่องหมายจุลภาคหรือตัวอักษรพิเศษใด ๆ แล้วถอดรหัสเมื่อมีการใช้งาน / แสดงผลในสื่อภาพใด ๆ

(เครื่องหมายจุลภาคกลายเป็น% 2C)

ทุกภาษาควรมีวิธีในการเข้ารหัส URL และถอดรหัสสตริง

เช่นในจาวา

URLEncoder.encode(myString,"UTF-8"); //to encode
URLDecoder.decode(myEncodedstring, "UTF-8"); //to decode

ฉันรู้ว่านี่เป็นวิธีแก้ปัญหาทั่วไปมากและอาจไม่เหมาะกับสถานการณ์ที่ผู้ใช้ต้องการดูเนื้อหาของไฟล์ csv ด้วยตนเอง


0

ฉันมักจะทำสิ่งนี้ในไฟล์ CSV ของฉันซึ่งเป็นขั้นตอนการแยกวิเคราะห์ สมมติว่าตัวแปร 'line' เป็นหนึ่งบรรทัดภายในไฟล์ CSV และค่าของคอลัมน์ทั้งหมดอยู่ในเครื่องหมายคำพูดคู่ หลังจากดำเนินการสองบรรทัดด้านล่างคุณจะได้รับคอลัมน์ CSV ในคอลเลกชัน 'ค่า'

// The below two lines will split the columns as well as trim the DBOULE QUOTES around values but NOT within them
    string trimmedLine = line.Trim(new char[] { '\"' });
    List<string> values = trimmedLine.Split(new string[] { "\",\"" }, StringSplitOptions.None).ToList();

1
ทำไมรหัสของฉันไม่แสดงเป็นหลายสีใน StackOverflow ฉันเยื้องด้วยช่องว่างสี่ช่อง
user1451111


0

วิธีแก้ปัญหาที่ง่ายที่สุดที่ฉันพบคือ LibreOffice ใช้:

  1. แทนที่ตัวอักษรทั้งหมด"ด้วย
  2. ใส่เครื่องหมายคำพูดคู่รอบสตริงของคุณ

คุณสามารถใช้สิ่งที่ Excel ใช้:

  1. แทนที่ตัวอักษรทั้งหมด"ด้วย""
  2. ใส่เครื่องหมายคำพูดคู่รอบสตริงของคุณ

สังเกตว่าคนอื่นแนะนำให้ทำตามขั้นตอนที่ 2 ข้างต้นเท่านั้น แต่ไม่สามารถใช้ได้กับบรรทัดที่"ตามด้วย a ,เช่นใน CSV ที่คุณต้องการให้มีคอลัมน์เดียวกับสตริงhello",worldเนื่องจาก CSV จะอ่าน:

"hello",world"

ซึ่งตีความว่าเป็นแถวที่มีสองคอลัมน์: helloและworld"


1
ตามกฎมาตรฐานฟิลด์ใด ๆ ที่มีอักขระแยกหรือเครื่องหมายคำพูดล้อมรอบด้วยเครื่องหมายคำพูดและเครื่องหมายคำพูดใด ๆ ที่อยู่ภายในเป็นสองเท่าดังนั้นจึงไม่มีปัญหา hello",worldฟิลด์ของคุณจะต้องได้รับการบันทึก"hello"",world"ซึ่งสามารถแยกวิเคราะห์ได้อย่างถูกต้อง 100%
Nyerguds

0
    public static IEnumerable<string> LineSplitter(this string line, char 
         separator, char skip = '"')
    {
        var fieldStart = 0;
        for (var i = 0; i < line.Length; i++)
        {
            if (line[i] == separator)
            {
                yield return line.Substring(fieldStart, i - fieldStart);
                fieldStart = i + 1;
            }
            else if (i == line.Length - 1)
            {
                yield return line.Substring(fieldStart, i - fieldStart + 1);
                fieldStart = i + 1;
            }

            if (line[i] == '"')
                for (i++; i < line.Length && line[i] != skip; i++) { }
        }

        if (line[line.Length - 1] == separator)
        {
            yield return string.Empty;
        }
    }

0

ฉันใช้ไลบรารี Csvreader แต่โดยใช้ว่าฉันได้รับข้อมูลโดยการระเบิดจากเครื่องหมายจุลภาค (,) ในค่าคอลัมน์

ดังนั้นหากคุณต้องการแทรกข้อมูลไฟล์ CSV ซึ่งมีเครื่องหมายจุลภาค (,) ในคอลัมน์ค่าส่วนใหญ่คุณสามารถใช้ฟังก์ชันด้านล่าง ผู้เขียนลิงค์ => https://gist.github.com/jaywilliams/385876

function csv_to_array($filename='', $delimiter=',')
{
    if(!file_exists($filename) || !is_readable($filename))
        return FALSE;

    $header = NULL;
    $data = array();
    if (($handle = fopen($filename, 'r')) !== FALSE)
    {
        while (($row = fgetcsv($handle, 1000, $delimiter)) !== FALSE)
        {
            if(!$header)
                $header = $row;
            else
                $data[] = array_combine($header, $row);
        }
        fclose($handle);
    }
    return $data;
}

0

ฉันใช้ไลบรารี papaParse เพื่อแยกวิเคราะห์ไฟล์ CSV และมีคู่คีย์ - ค่า (คีย์ / ส่วนหัว / แถวแรกของไฟล์ CSV ค่า)

นี่คือตัวอย่างที่ฉันใช้:

https://codesandbox.io/embed/llqmrp96pm

มีไฟล์ dummy.csv อยู่ที่นั่นเพื่อสาธิตการแยกวิเคราะห์ CSV

ฉันใช้มันภายใน reactJS แม้ว่ามันจะง่ายและง่ายในการทำซ้ำในแอพที่เขียนด้วยภาษาใด ๆ


0

ตัวอย่างอาจช่วยแสดงว่าเครื่องหมายจุลภาคสามารถแสดงในไฟล์. csv ได้อย่างไร สร้างไฟล์ข้อความอย่างง่ายดังนี้

บันทึกไฟล์ข้อความนี้เป็นไฟล์ข้อความที่มีคำต่อท้าย ".csv" และเปิดด้วย Excel 2000 จาก Windows 10

aa, bb, cc, d; d "ในการนำเสนอสเปรดชีตบรรทัดด้านล่างควรมีลักษณะเป็นบรรทัดด้านบนยกเว้นด้านล่างแสดงเครื่องหมายจุลภาคที่แสดงแทนที่จะเป็นเครื่องหมายอัฒภาคระหว่าง d" aa, bb, cc, "d, d", ใช้ได้แม้ใน Excel

aa, bb, cc, "d, d", มันใช้ได้แม้ใน Excel 2000 aa, bb, cc, "d, d", มันใช้ได้แม้ใน Excel 2000 aa, bb, cc, "d, d", มันใช้งานได้ แม้ใน Excel 2000

aa, bb, cc, "d, d", สิ่งนี้ล้มเหลวใน Excel 2000 เนื่องจากช่องว่างอ้างคำที่ 1 aa, bb, cc, "d, d", สิ่งนี้ล้มเหลวใน Excel 2000 เนื่องจากช่องว่างอ้างคำที่ 1 aa, bb, cc, "d, d", สิ่งนี้ล้มเหลวใน Excel 2000 เนื่องจากช่องว่างอ้างคำที่ 1

aa, bb, cc, "d, d" สิ่งนี้ใช้ได้แม้ใน Excel 2000 แม้จะมีช่องว่างก่อนและหลังเครื่องหมายคำพูดที่ 2 aa, bb, cc, "d, d" สิ่งนี้ใช้ได้แม้ใน Excel 2000 แม้จะมีช่องว่างก่อนและหลังเครื่องหมายคำพูดที่ 2 aa, bb, cc, "d, d" สิ่งนี้ใช้ได้แม้ใน Excel 2000 แม้จะมีช่องว่างก่อนและหลังเครื่องหมายคำพูดที่ 2

กฎ: หากคุณต้องการแสดงเครื่องหมายจุลภาคในเซลล์ aa (เขตข้อมูล) ของไฟล์. csv: "เริ่มต้นและจบฟิลด์ด้วยเครื่องหมายคำพูดคู่ แต่หลีกเลี่ยงพื้นที่สีขาวก่อนเครื่องหมายคำพูดที่ 1"


-1

ฉันคิดว่าทางออกที่ง่ายที่สุดสำหรับปัญหานี้คือให้ลูกค้าเปิด csv ใน excel แล้ว ctrl + r เพื่อแทนที่จุลภาคทั้งหมดด้วยตัวระบุที่คุณต้องการ นี่เป็นเรื่องง่ายสำหรับลูกค้าและต้องการการเปลี่ยนแปลงเพียงรหัสเดียวเพื่ออ่านตัวคั่นที่คุณเลือก


ใครบอกว่าพวกเขามี Excel? ในความเป็นจริงใครบอกว่ามันเป็นมนุษย์ที่กำลังทำการอัพโหลด ...
bytedev

-3

ใช้อักขระแท็บ (\ t) เพื่อแยกฟิลด์


4
-1 ยอดเยี่ยมจนกระทั่งมีคนใช้แท็บในค่าของพวกเขาจากนั้นคุณกลับไปยังปัญหาที่บุคคลถามคำถามได้รับ การปัดถ่านตัวคั่นหนึ่งไปยังอีกตัวจะไม่สามารถแก้ปัญหาได้
bytedev

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

6
"คนไม่สามารถใส่แท็บในการป้อนข้อมูล" .... คุณจริงจังหรือไม่? A) แน่นอนว่าคน ๆ หนึ่งสามารถใส่แท็บในฟิลด์อินพุต B) ใครบอกว่ามันเป็น GUI ที่ข้อมูลมาจากไหน? C) ใครบอกว่ามันเป็นมนุษย์ที่กำลังป้อนข้อมูลอยู่
bytedev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.