J'obtiens des données sous forme de chaîne à partir d'un appareil distant. J'ai besoin d'analyser les données. Les données se présentent généralement comme suit:
def getCommandResult(tgdatas) tgdatas_arr = tgdatas.split("\r\n") tsgs = tgdatas_arr[5..tgdatas_arr.index("END")-2] tsgs.map! {|tsg| tsg.gsub(/\s+/, " ").split(" ")[0] } return tsgs end
J'adorerai avoir les données sous forme de tableau de tableaux ou de toute autre structure sensible par programme.
Je divise les données en un tableau utilisant:
["RXOTRX-59-9", "EK0322", "LOCAL MODE"]
puis en supprimant l'espace supplémentaire sur chaque élément du tableau avec:
RXOTRX-59-9 EK0322 LOCAL MODE
mais ceci a une limitation en ce que les cellules vides ne sont pas prises en compte. Je m'attends à ce que le tableau contienne cinq éléments, mais à la place il en contient moins de cinq.
Cas 1: Dans ce cas, j'obtiens le résultat attendu:
["RXOTG-59", "59", "0", "EK0322", "ABIS PATH FAULT"]
se convertit en
RXOTG-59 59 0 EK0322 ABIS PATH FAULT
Cas 2: Dans ce cas, j'obtiens un résultat inattendu:
tsgs.map! {|tsg| tsg.gsub(/\s+/, " ").split(" ") }
se convertit en
str.split("\r\n")
MO SCGR SC RSITE ALARM_SITUATION RXOTG-59 59 0 EK0322 ABIS PATH FAULT RXOCF-59 EK0322 LOCAL MODE RXOTRX-59-0 4 EK0322 LOCAL MODE RXOTRX-59-1 EK0322 LOCAL MODE RXOTRX-59-4 0 EK0322 LOCAL MODE RXOTRX-59-5 1 3 EK0322 LOCAL MODE RXOTRX-59-8 EK0322 LOCAL MODE RXOTRX-59-9 EK0322 LOCAL MODE
3 Réponses :
Essayez si cela peut être viable pour vous, étant donné le data_string
:
headers = data[0] body = data[1..] body.map { |line| headers.map(&:to_sym).zip(line).to_h } #=> [{:MO=>"RXOTG-59", :SCGR=>"59", :SC=>"0", :RSITE=>"EK0322", :ALARM_SITUATION=>"ABIS PATH FAULT"}, {:MO=>"RXOCF-59", :SCGR=>"", :SC=>"", :RSITE=>"EK0322", :ALARM_SITUATION=>"LOCAL MODE"}, ...
Définissez le point de départ de chaque ligne, car il semble être aligné avec le en-tête.
# [["MO", "SCGR", "SC", "RSITE", "ALARM_SITUATION"] # ["RXOTG-59", "59", "0", "EK0322", "ABIS PATH FAULT"] # ["RXOCF-59", "", "", "EK0322", "LOCAL MODE"] # ["RXOTRX-59-0", "4", "", "EK0322", "LOCAL MODE"] # ["RXOTRX-59-1", "", "", "EK0322", "LOCAL MODE"] # ["RXOTRX-59-4", "", "0", "EK0322", "LOCAL MODE"] # ["RXOTRX-59-5", "1", "3", "EK0322", "LOCAL MODE"] # ["RXOTRX-59-8", "", "", "EK0322", "LOCAL MODE"] # ["RXOTRX-59-9", "", "", "EK0322", "LOCAL MODE"]]
Ensuite, mappez chaque ligne en considérant les points de départ, en supprimant les espaces de fin:
data = data.map { |line| starts.each_cons(2).map { |a,b| line[a..b-1].strip } }
Vous allez donc finir avec ce tableau:
data = data_string.split("\n") starts = [0, 18, 24, 35, 51, (data.map(&:size)).max ]
Vous pouvez ensuite le convertir en hachage ou utiliser la bibliothèque csv pour manipuler vos données.
Voici un moyen de générer un tableau de hachages:
data_string = "MO SCGR SC RSITE ALARM_SITUATION\nRXOTG-59 59 0 EK0322 ABIS PATH FAULT\nRXOCF-59 EK0322 LOCAL MODE\nRXOTRX-59-0 4 EK0322 LOCAL MODE\nRXOTRX-59-1 EK0322 LOCAL MODE\nRXOTRX-59-4 0 EK0322 LOCAL MODE\nRXOTRX-59-5 1 3 EK0322 LOCAL MODE\nRXOTRX-59-8 EK0322 LOCAL MODE\nRXOTRX-59-9 EK0322 LOCAL MODE"
Ce serait mieux si vous pouvez obtenir les index dans démarre
automatiquement en utilisant les informations d'en-tête. Vous voyez que chaque nom de colonne n'inclut pas d'espace? De plus, le code compliqué pour obtenir le dernier index n'est pas nécessaire. Vous pouvez y avoir 0
.
@sawa, merci d'avoir indiqué que 0
fonctionne quand même comme dernier point de départ. Je n'ai pas ajouté d'automatisation pour trouver les points de départ car n'étant pas à l'aise avec les expressions régulières, j'ai trouvé un moche: data [0] .each_char.each_cons (2) .with_index.with_object ([]) {| ((a , b), i), res | res << i + 1 if (a == '' && b! = '')}
. Alors, j'ai décidé de passer.
String.unpack avec directive " Un "est bien pour les chaînes de largeur fixe.
str = "RXOTRX-59-9 EK0322 LOCAL MODE" p str.unpack("A20A4A11A16A15" ) # => ["RXOTRX-59-9", "", "", "EK0322", "LOCAL MODE"]
C'est bon à savoir, très utile. J'ai remarqué que si la longueur de la chaîne est inférieure à 20 + 4 + 11 + 16 + 15
, unpack
remplit efficacement la chaîne avec des espaces pour en faire la longueur indiquée avant exécuter la directive (une commodité).
Votre chaîne 1 , légèrement modifiée:
csv = CSV.new(csv_data, headers: true, header_converters: :symbol, converters: :all) #=> <#CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:", # " row_sep:"\n" quote_char:"\"" headers:true> a = csv.to_a #=> [#<CSV::Row mo:"RXOTG-59" scgr:59 sc:0 rsite:"EK0322" alarm_situation:"ABIS PATH FAULT">, # #<CSV::Row mo:"RXOCF-59" scgr:nil sc:nil rsite:"EK0322" alarm_situation:"LOCAL MODE">, # ... # #<CSV::Row mo:"RXOTRX-59-9" scgr:nil sc:nil rsite:"EK0322" alarm_situation:"LOCAL MODE">] a.map(&:to_h) #=> < hash shown above >
Cette chaîne ressemble beaucoup à une structure de données CSV, nous pourrions donc être tentés de la convertir en chaîne CSV , nous permettant ainsi de mettre en œuvre les méthodes fournies par le Classe CSV .
Convertir la chaîne en chaîne CSV
Code
require 'csv' CSV.new(csv_data, headers: true, header_converters: :symbol, converters: :all).to_a.map(&:to_h) #=> [{:mo=>"RXOTG-59", :scgr=>59, :sc=>0, :rsite=>"EK0322", # :alarm_situation=>"ABIS PATH FAULT"}, # {:mo=>"RXOCF-59", :scgr=>nil, :sc=>nil, :rsite=>"EK0322", # :alarm_situation=>"LOCAL MODE"}, # {:mo=>"RXOTRX-59-0", :scgr=>4, :sc=>nil, :rsite=>"EK0322", # :alarm_situation=>"LOCAL MODE"}, # {:mo=>"RXOTRX-59-1", :scgr=>nil, :sc=>nil, :rsite=>"EK0322", # :alarm_situation=>"LOCAL MODE"}, # {:mo=>"RXOTRX-59-4", :scgr=>nil, :sc=>0, :rsite=>nil, # :alarm_situation=>nil}, # {:mo=>"RXOTRX-59-5", :scgr=>1, :sc=>3, :rsite=>nil"EK0322", # :alarm_situation=>"LOCAL MODE"}, # {:mo=>"RXOTRX-59-8", :scgr=>nil, :sc=>nil, :rsite=>"EK0322", # :alarm_situation=>"LOCAL MODE"}, # {:mo=>"RXOTRX-59-9", :scgr=>nil, :sc=>nil, :rsite=>"EK0322", # :alarm_situation=>"LOCAL MODE"}]
Convertir la chaîne
Convertissez maintenant la chaîne data
en chaîne CSV.
s = "RXOTRX-59-4 0" s.size #=> 25 cols #=> [17, 23, 34, 50] cols.each { |i| s[i] = ',' if s.size > i+1 } s #=> "RXOTRX-59-4 , ,0" s.gsub(/ *, */,',') #=> "RXOTRX-59-4,,0"
Notez que @iGian avait la même idée de convertir la chaîne en chaîne CSV dans sa réponse précédente.
J'ai remarqué que la méthode convert_to_csv n'a pas d'instruction de retour, y a-t-il une raison à cela?
On pourrait écrire return data.each_line.map do | s | ... end.join
, mais le mot-clé return
est redondant car data.each_line.map do | s | ... end.join
est la dernière expression évaluée dans la méthode. Sa valeur de retour est donc la valeur de retour de la méthode.
Salut, j'ai remarqué une dernière chose, lorsque le champ ALARM_SITUATION est vide, je veux dire que sans même un espace vide, il ne fonctionne pas, vérifiez ce lien, rextester.com/YAB84581
Emmanuel, merci pour le heads-up. J'ai corrigé cela et modifié légèrement l'exemple pour illustrer le problème.
Il y avait une proposition de modification qui avait été rejetée par les modérateurs avant que je ne l'ai vue. Je n'ai pas remarqué qui l'a posté et il semble maintenant avoir disparu. Je tiens à remercier celui qui l'a posté, car je pense qu'il était lié à un problème qu'Emmanuel a noté dans un commentaire ci-dessus (que j'ai corrigé par la suite). Je pense qu'il est généralement préférable, cependant, d'informer l'auteur d'un problème dans un commentaire, suggérant éventuellement une solution, mais laissez l'auteur effectuer la modification nécessaire.