AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79493865
Accepted
user26136009
user26136009
Asked: 2025-03-08 12:13:34 +0800 CST2025-03-08 12:13:34 +0800 CST 2025-03-08 12:13:34 +0800 CST

Analisar um arquivo CSV que pode ter colunas fora da ordem esperada

  • 772

Eu uso a função fgetcsv() para dividir um arquivo .CSV em uma matriz para poder inserir os valores em um banco de dados.

O problema é que às vezes a corporação da qual eu baixo esses arquivos altera colunas em torno das quais altera o número do array. Aqui está um exemplo de um print_r do arquivo CSV:

Array ( [0] => Symbol [1] => Description [2] => Qty (Quantity) [3] => Price [4] => Price Chng % (Price Change %) [5] => Price Chng (Price Change ) [6] => Day Chng % (Day Change %) [7] => Day Chng (Day Change ) [8] => Cost Basis [9] => Gain % (Gain/Loss %) [10] => Gain (Gain/Loss ) [11] => Reinvest? [12] => Reinvest Capital Gains? [13] => Last Div (Last Dividend) [14] => Volume [15] => Security Type ) 
Array ( [0] => Test1 [1] => test desc [2] => 820 [3] => 19.505 [4] => -1.84% [5] => -0.365 [6] => -1.84% [7] => -300.37 [8] => 601.73 [9] => -18.4% [10] => -607.63 [11] => No [13] => 0.72025 [14] => 7041528 [15] => ETFs & Closed End Funds ) 
Array ( [0] => Test2 [1] => test desc again [2] => 110 [3] => 49.715 [4] => -1.83% [5] => -0.925 [6] => -1.76% [7] => -98.00 [8] => 95.2 [9] => -8.78% [10] => -526.55 [11] => Yes [14] => 28668328 [15] => ETFs & Closed End Funds ) 
Array ( [0] => Test3 [1] => test desc example [2] => 740 [3] => 21.71 [4] => -3.98% [5] => -0.9 [6] => -3.93% [7] => -657.59 [8] => 242.13 [9] => -20.63% [10] => -4176.73 [11] => No [13] => 2.0216 [14] => 2759846 [15] => ETFs & Closed End Funds ) 

Você pode ver que na segunda matriz há títulos como $column[0] é Símbolo e $column[3] é Preço.

[0] => Symbol [1] => Description [2] => Qty (Quantity) [3] => Price

Posso adicionar código para que $column[0] por exemplo sempre exiba o nome do símbolo? Se a corporação decidir adicionar uma coluna ou reorganizar as coisas, ela mudará a coluna Price para outra, como movê-la de $column[3] para $column[5]. Estou supondo que haja uma maneira de fazer com que ela pesquise qualquer chave que corresponda a uma coluna como Volume por exemplo, então qualquer chave que seja, ela poderia garantir que todas as chaves em cada matriz para Volume corresponderão a esse número de chave.

Eu estava apenas alterando manualmente os números da matriz quando eles fizeram isso, mas eles mudam muito, então estou pensando se posso adicionar algum código para onde a coluna Symbol seria sempre 0 e Price sempre estaria na coluna 3 e o mesmo se aplica a todos os nomes de coluna. Se eles forem reorganizados, eles ainda terão o mesmo número de antes?

Um pequeno exemplo do arquivo CSV:

"Symbol","Description","Qty (Quantity)","Price","Price Chng % (Price Change %)","Price Chng $ (Price Change $)","Day Chng % (Day Change %)","Day Chng $ (Day Change $)","Cost Basis","Gain % (Gain/Loss %)","Gain $ (Gain/Loss $)","Reinvest?","Reinvest Capital Gains?","Last Div (Last Dividend)","Volume","Security Type"
"Test1","ETF","820","$19.39","-2.42%","-$0.48","-2.42%","-$393.6","$601.73","-18.89%","-$3701.93","No","--","$0.72025","14,626,464","ETFs & Closed End Funds"
"Test2"," ETF","110","$49.43","-2.39%","-$1.21","-2.39%","-$133.1","$995.2","-9.31%","-$557.9","Yes","--","N/A","59,351,095","ETFs & Closed End Funds"
"Test3"," ETF","760","$21.77","-3.72%","-$0.84","-3.72%","-$638.4","$687.73","-20.02%","-$142.53","No","--","$2.0216","6,402,084","ETFs & Closed End Funds"
  • 3 3 respostas
  • 96 Views

3 respostas

  • Voted
  1. hakre
    2025-03-09T05:28:48+08:002025-03-09T05:28:48+08:00

    Eu estava apenas alterando manualmente os números da matriz quando eles fizeram isso, mas eles mudam muito, então estou pensando se posso adicionar algum código para que a coluna Símbolo seja sempre 0 e Preço esteja sempre na coluna 3 e o mesmo se aplique a todos os nomes de coluna.

    Na verdade, não é muita mágica, talvez a percepção do seu próprio histórico de tentativas (e erros) bloqueie um pouco sua mente.

    Vamos ver se conseguimos mudar isso.

    Primeiro, as boas notícias: especialmente com seu array, isso é simples, pois o arquivo CSV já foi analisado.

    E tudo isso se resume a uma única função, array_keys() .

    Bom!

    Mas primeiro as coisas mais importantes. Vamos deixar claro que estamos falando de um array aqui:

    if (!is_array($array))
    {
        throw new Error('Array required, got ' . gettype($array));
    }
    

    Ok, talvez nenhuma novidade até agora. Mas é melhor prevenir do que remediar!

    Agora, vamos deixar outra coisa clara: esta $arrayé uma lista :

    if (!array_is_list($array))
    {
        throw new Error('Array is not a list');
    }
    

    Uma lista significa que cada chave no array é um número inteiro consecutivo começando em zero. Ou seja, cada chave tem o valor da chave anterior mais um (+1 ou ++).

    E se não houvesse uma chave anterior, esse número virtualmente anterior seria menos um (-1).

    Um array sem membros é um exemplo de tal lista , assim como seria o valor de retorno de array("one") ou array("one", "two") etc.

    Para matemáticos parece bem natural , eles só precisam especificar se o número zero faz parte de números naturais . Em PHP, o primeiro índice (chave) do array é sempre zero (0).

    Então, falando sobre array_keys() aqui, para um array não vazio, podemos sempre dizer:

    assert(is_array($array) && !empty($array));
    
    range(0, -1 + count($array)) === array_keys($array);
                                     //^^^^^^^^(......)
    

    Ou para entender melhor array_keys() , pois ele retorna a lista de chaves:

    array_keys($array) === array_keys(array_keys($array));
    //^^^^^^^^(......)     ^^^^^^^^^^(^^^^^^^^^^(......))
    

    Bom saber!

    Mas chega de recursão array_keys() por enquanto e vamos dar uma olhada no panorama geral desse adorado array.

    O dado $arrayé todo analisado em CSV, com o seguinte layout comum de um arquivo CSV:

    TITLE
    [... TEXT]
    
    SYMBOLS
    [... DATA]
    

    Este layout é baseado em linhas e $arrayhá uma lista (veja acima) dessas linhas.

    Vamos primeiro obter o título, como ele é o primeiro na lista, podemos atribuí-lo com desconstrução de lista. Na verdade, parece atribuir a um array:

    [$title] = $array;
    

    Agora, a próxima coisa é encontrar os SÍMBOLOS . Estamos procurando a primeira linha vazia.

    [$indexOfFirstEmpty] = array_keys($array, null);
    

    Bom saber: array_keys() em ação para as chaves de valor .

    E então tenho que admitir que te traí: não se trata apenas de array_keys() , há também array_slice() e array_shift() :

    $data = array_slice($array, 1 + $indexOfFirstEmpty);
    $symbols = array_shift($data);
    

    Ok, provavelmente está um pouco rápido agora, todas essas funções permanecem documentadas no manual do PHP em https://php.net/manual , portanto, reserve um tempo para ler se precisar. Você sempre pode continuar mais tarde aqui.

    Vamos dar uma olhada no que temos até agora:

    print_r($symbols);
    
    Array
    (
        [0] => Symbol
        [1] => Description
        [2] => Qty (Quantity)
        [3] => Price
        [4] => Price Chng % (Price Change %)
        [5] => Price Chng (Price Change )
    [ ... 11 more lines ]
    
    print_r($data);
    
    Array
    (
        [0] => Array
            (
                [0] => Test1
                [1] => test desc
                [2] => 820
                [3] => 19.505
    [ ... 51 more lines ]
    

    A saída já nos mostra como as chaves de símbolos correspondem às chaves de dados.

    Se os de cada data puderem ser combinados com seu símbolo, você obtém o mapeamento e pode acessá-lo pelo nome ao lado do índice numérico.

    Como cada símbolo (string) representará uma chave de array, os símbolos precisam ser verificados se estão se encaixando, por exemplo, um símbolo duplicado arruinaria o jogo. Virada de mesa:

    $keys = array_flip($symbols);
    assert($symbols === array_keys($keys));
    

    Se isso for afirmado, podemos prosseguir e usar as chaves por símbolo (string) para obter o índice (inteiro). Caso não haja tal índice, podemos usar o operador de coalescência nulo e NULL como fallback.

    foreach ($data as $index => $date)
    {
        printf(
            "#%d: %s %s\n",
            $index,
            var_export($date[$keys['Symbol']] ?? null, true),
            var_export($date[$keys['Last Div (Last Dividend)']] ?? null, true),
        );
    }
    
    #0: 'Test1' '0.72025'
    #1: 'Test2' NULL
    #2: 'Test3' '2.0216'
    

    Até agora para os arrays. No geral, funciona assim.

    Naturalmente é possível resumir isso melhor, mas aí fica mais abstrato.

    Por exemplo, eu pessoalmente prefiro a interface ArrayAccess para chaves simbólicas (aliasing de chave) e SplFileObject para valores separados por vírgula (CSV).

    Pelo que me lembro, um exemplo que coloquei no Stackoverflow foi Process CSV Into Array With Column Headings For Key .

    • 1
  2. Best Answer
    mickmackusa
    2025-03-09T05:47:35+08:002025-03-09T05:47:35+08:00

    Se seus dados de entrada não forem estruturados de forma confiável, use uma lista de permissões de colunas esperadas na ordem desejada para filtrar e mapear os dados analisados. Demo >= PHP7.4 , Demo PHP7 - PHP8.3 , Demo PHP5.2 - PHP8.3

    $whitelist = [
        "Symbol",
        "Description",
        "Qty (Quantity)",
        "Price",
        "Price Chng % (Price Change %)",
        "Price Chng $ (Price Change $)",
        "Day Chng % (Day Change %)",
        "Day Chng $ (Day Change $)",
        "Cost Basis",
        "Gain % (Gain/Loss %)",
        "Gain $ (Gain/Loss $)",
        "Reinvest?",
        "Reinvest Capital Gains?",
        "Last Div (Last Dividend)",
        "Volume",
        "Security Type"
    ];
    
    $result = [];
    if (($handle = fopen("file.csv", "r")) !== false) {
        $headers = fgetcsv($handle, escape: '') ?: [];
        $map = array_flip($headers);
        while (($values = fgetcsv($handle, escape: '')) !== false) {
            $row = [];
            foreach ($whitelist as $col) {
                $row[$col] = $values[$map[$col]] ?? null;
            }
            $result[] = $row;
        }
        fclose($handle);
    }
    var_export($result);
    

    Conteúdo do arquivo:

    "Price","Bogus","Symbol","Description","Qty (Quantity)","Price Chng % (Price Change %)","Price Chng $ (Price Change $)","Day Chng % (Day Change %)","Day Chng $ (Day Change $)","Cost Basis","Gain % (Gain/Loss %)","Gain $ (Gain/Loss $)","Reinvest?","Reinvest Capital Gains?","Last Div (Last Dividend)","Volume","Security Type"
    "$19.39","foo1","Test1","ETF","820","-2.42%","-$0.48","-2.42%","-$393.6","$601.73","-18.89%","-$3701.93","No","--","$0.72025","14,626,464","ETFs & Closed End Funds"
    "$49.43","foo2","Test2"," ETF","110","-2.39%","-$1.21","-2.39%","-$133.1","$995.2","-9.31%","-$557.9","Yes","--","N/A","59,351,095","ETFs & Closed End Funds"
    "$21.77","foo3","Test3"," ETF","760","-3.72%","-$0.84","-3.72%","-$638.4","$687.73","-20.02%","-$142.53","No","--","$2.0216","6,402,084","ETFs & Closed End Funds"
    

    Saída:

    array (
      0 => 
      array (
        'Symbol' => 'Test1',
        'Description' => 'ETF',
        'Qty (Quantity)' => '820',
        'Price' => '$19.39',
        'Price Chng % (Price Change %)' => '-2.42%',
        'Price Chng $ (Price Change $)' => '-$0.48',
        'Day Chng % (Day Change %)' => '-2.42%',
        'Day Chng $ (Day Change $)' => '-$393.6',
        'Cost Basis' => '$601.73',
        'Gain % (Gain/Loss %)' => '-18.89%',
        'Gain $ (Gain/Loss $)' => '-$3701.93',
        'Reinvest?' => 'No',
        'Reinvest Capital Gains?' => '--',
        'Last Div (Last Dividend)' => '$0.72025',
        'Volume' => '14,626,464',
        'Security Type' => 'ETFs & Closed End Funds',
      ),
      1 => 
      array (
        'Symbol' => 'Test2',
        'Description' => ' ETF',
        'Qty (Quantity)' => '110',
        'Price' => '$49.43',
        'Price Chng % (Price Change %)' => '-2.39%',
        'Price Chng $ (Price Change $)' => '-$1.21',
        'Day Chng % (Day Change %)' => '-2.39%',
        'Day Chng $ (Day Change $)' => '-$133.1',
        'Cost Basis' => '$995.2',
        'Gain % (Gain/Loss %)' => '-9.31%',
        'Gain $ (Gain/Loss $)' => '-$557.9',
        'Reinvest?' => 'Yes',
        'Reinvest Capital Gains?' => '--',
        'Last Div (Last Dividend)' => 'N/A',
        'Volume' => '59,351,095',
        'Security Type' => 'ETFs & Closed End Funds',
      ),
      2 => 
      array (
        'Symbol' => 'Test3',
        'Description' => ' ETF',
        'Qty (Quantity)' => '760',
        'Price' => '$21.77',
        'Price Chng % (Price Change %)' => '-3.72%',
        'Price Chng $ (Price Change $)' => '-$0.84',
        'Day Chng % (Day Change %)' => '-3.72%',
        'Day Chng $ (Day Change $)' => '-$638.4',
        'Cost Basis' => '$687.73',
        'Gain % (Gain/Loss %)' => '-20.02%',
        'Gain $ (Gain/Loss $)' => '-$142.53',
        'Reinvest?' => 'No',
        'Reinvest Capital Gains?' => '--',
        'Last Div (Last Dividend)' => '$2.0216',
        'Volume' => '6,402,084',
        'Security Type' => 'ETFs & Closed End Funds',
      ),
    )
    
    • 1
  3. AbuBakr
    2025-03-08T12:37:04+08:002025-03-08T12:37:04+08:00

    Você pode mapear dinamicamente posições de colunas usando a linha de cabeçalho. Primeiro, obtenha as posições de cabeçalho, depois use essas posições para acessar valores em cada linha.

    if (($handle = fopen("file.csv", "r")) !== FALSE) {
        $headers = fgetcsv($handle); // Read header row
        $map = array_flip($headers); // Map column names to indexes
    
        while (($data = fgetcsv($handle)) !== FALSE) {
            $symbol = $data[$map['Symbol']] ?? null;
            $price = $data[$map['Price']] ?? null;
            echo "Symbol: $symbol, Price: $price\n";
        }
        fclose($handle);
    }
    
    • 0

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve