This is a follow up to the question asked here: Groovy parsing text file
The difference now is my file has a header, I attempted to read past the header first and then onto the content that I am wanting, but for some reason it doesn't seem to cooperate.
def dataList = []
def theInfoName = 'testdata.txt'
boolean headersDone = false //header set to false by default
File theInfoFile = new File( theInfoName )
if( !theInfoFile.exists() ) {
println "File does not exist"
} else {
def driveInfo = [:]
// Step through each line in the file
theInfoFile.eachLine { line ->
//this is where im trying to account for the header
if(!headersDone) { //look if line contains "..." if it does that turn headersDone to true
if(line.contains("...")) {
headersDone = true
}
} else {
// If the line isn't blank
if( line.trim() ) {
// Split into a key and value
def (key,value) = line.split( '\t: ' ).collect { it.trim() }
// and store them in the driveInfo Map
driveInfo."$key" = value
}
else {
// If the line is blank, and we have some info
if( driveInfo ) {
// store it in the list
dataList << driveInfo
// and clear it
driveInfo = [:]
}
}
}
// when we've finished the file, store any remaining data
if( driveInfo ) {
dataList << driveInfo
}
}
dataList.eachWithIndex { it, index ->
println "Drive $index"
it.each { k, v ->
println "\t$k = $v"
}
}
I tried it out with the code provided in the previous post to make sure it wasn't something I was doing differently and it came with the same output.
What happens is that it posts the same block of information 11 times.
The header looks is the following:
Random date information here with some other info
Slightly more random information followed by
Examining hard disk information ...
HDD Device 0 : /dev/sda
HDD Model ID : ST3160815A
HDD Serial No : 5RA020QY
HDD Revision : 3.AAA
HDD Size : 152628 MB
Interface : IDE/ATA
Temperature : 33 C
Health : 100%
Performance : 70%
Power on Time : 27 days, 13 hours
Est. Lifetime : more than 1000 days
HDD Device 1 : /dev/sdb
HDD Model ID : TOSHIBA MK1237GSX
HDD Serial No : 97LVF9MHS
HDD Revision : DL130M
HDD Size : 114473 MB
Interface : S-ATA
Temperature : 30 C
Health : 100%
Performance : 100%
Power on Time : 38 days, 11 hours
Est. Lifetime : more than 1000 days
Does anyone know why it is printing out duplicate of the information?
The problem is the addition of the "last" driveInfo to the dataList:
// when we've finished the file, store any remaining data
if( driveInfo ) {
dataList << driveInfo
}
It has to be one curly brace below its current position, otherwise it belongs to the eachLine closure.
Can't see anything obviously wrong with the code. I suggest to add a couple of printlns so you can see how the maps, lists and variables change. That might give you an idea where the bug might be.
Related
I have txt file for example:
useless text
-- modelName
model parameters
model parameters
-- modelName
model parameters
model parameters
e.t.c
I need to split this file into List where the elements of the list is model with model parametrs.
My algoritm for this
File(FILEPATH).eachLine { line ->
if (line =regExpForModelName) {
while(line!=regExpForModelName)
model.add(line)
}
}
while(line!=regExpForModelName) is clearly wrong
You could do it with a simple state machine like so:
enum State {
SCANNING, PARSING
}
def parseFile(String filename) {
def key = null
def result = []
def mode = State.SCANNING
new File(filename).eachLine { line ->
switch(mode) {
case State.SCANNING:
if(line.startsWith('--')) {
key = (line - '--').trim()
result << [name:key, lines:[]]
mode = State.PARSING
}
break
case State.PARSING:
if(line.size() == 0) {
mode = State.SCANNING
}
else {
result[-1].lines << line
}
}
result
}
}
def results = parseFile('/tmp/file.txt')
results.each {
println it
}
So it starts off SCANNING, and then when it finds a header item, it adds a new element to the list and switches to PARSING
Then it keeps PARSING (and adding lines to the list) until it hits an empty line, when it switches back into a SCANNING state
I'm trying to trim a mp3 file.
using this code:
private void TrimMp3(string open, string save)
{
using (var mp3FileReader = new Mp3FileReader(open))
using (var writer = File.Create(save))
{
var startPostion = TimeSpan.FromSeconds(60);
var endPostion = TimeSpan.FromSeconds(90);
mp3FileReader.CurrentTime = startPostion;
while (mp3FileReader.CurrentTime < endPostion)
{
var frame = mp3FileReader.ReadNextFrame();
if (frame == null) break;
writer.Write(frame.RawData, 0, frame.RawData.Length);
}
}
}
"open" is the file I'm trimming and "save" is the location I'm saving.
The trimming works but not fully. The new file does start from 60 seconds but it keeps going and not stopping at 90 seconds. For example if the file is 3 minutes it will start at 1 minute and end at 3. Its like the while is always true. What am I doing wrong here?
Thanks in advance!
I have no idea what your Mp3FileReader is doing there. But your while loop looks odd. Does mp3FileRead.ReadNextFrame() also change mp3FileReader.CurrentTime ? If not then there is your problem.
You should atleast do mp3FileReader.CurrentTime + 1Frame. Otherwise your currenttime is never changed and loop will always be true
In NAudio 1.8.0, Mp3FileReader.ReadNextFrame does not progress CurrentTime, although I checked in a fix for that recently.
So you can either get the latest NAudio code, or make use of the SampleCount property on each Mp3Frame to accurately keep track of how far through you are yourself.
NOTE: See amended post below re: Hardware mirroring
I have written two Swift functions that toggle the display mirroring in OSX. Both work; the difference between them is just syntax when dealing with pointers. For the convenience of those interested in learning how to toggle mirroring in Swift, I have included the text of the playground file below.
My question is about memory allocation. Here is the section of interest:
toggleMirroringUgly
// allocate space for array
let displayListPtr = displayIDListPtr.alloc(Int(displayCount)) //see typealias above
// fill the list
postError(CGGetActiveDisplayList(displayCount, displayListPtr, &activeCount))
toggleMirroring
// allocate space for list of displays
var displayIDList = Array<CGDirectDisplayID>(count: Int(displayCount), repeatedValue: kCGNullDirectDisplay)
// fill the list
postError(CGGetActiveDisplayList(displayCount, &displayIDList, &activeCount))
CGGetActiveDisplayList is a low-level function call that relies on data being arranged in consecutive memory locations. I am reasonably confident that “alloc” from the ugly version is contiguous. Empirically, it seems that the “Array(…)” call is also contiguous, but can I rely on that always being true (e.g., if the number of displays grows)? Is this assumption about the Swift array initializer poor form?
Here’s all the code; apologies for formatting issues. Note that only one of the two functions should be called; otherwise, you’ll end up where you started.
//: Playground - noun: a place where people can play
import Cocoa
// apparently not defined in Swift version of SDK 10.11 (XCode 7.3.1), so add manually
let kCGNullDirectDisplay = CGDirectDisplayID(0)
let kCGDirectMainDisplay = CGMainDisplayID() // not used here, just for the record
let maxDisplays:UInt32 = 20 // not used
var onlineCount:UInt32 = 0 // not used
func postError(error : CGError){
if error != CGError.Success {
print("got an error")
}
}
// this toggles all active displays, online or not
func toggleMirroring(){
var displayCount:UInt32 = 0
var activeCount:UInt32 = 0
//var onlineCount:UInt32 = 0 //not used
//get count of active displays (by passing nil to CGGetActiveDisplayList
postError(CGGetActiveDisplayList(0, nil, &displayCount))
if displayCount < 2 { return } // no point in any mirroring functions
//***
// allocate space for list of displays
var displayIDList = Array<CGDirectDisplayID>(count: Int(displayCount), repeatedValue: kCGNullDirectDisplay)
// fill the list
postError(CGGetActiveDisplayList(displayCount, &displayIDList, &activeCount))
//***
// determine if mirroring is active
// hack to convert from boolean_t (aka UInt32) to swift's bool
let displaysMirrored = CGDisplayIsInMirrorSet(CGMainDisplayID()) != 0
// set master based on current mirroring state
// if mirroring, master = null, if not, master = main display
let master = (true == displaysMirrored) ? kCGNullDirectDisplay : CGMainDisplayID()
// start the configuration
var configRef:CGDisplayConfigRef = nil //swift 3 syntax
postError(CGBeginDisplayConfiguration(&configRef));
for i in 0..<Int(displayCount) {
let currentDisplay = CGDirectDisplayID(displayIDList[i])
if CGMainDisplayID() != currentDisplay {
CGConfigureDisplayMirrorOfDisplay(configRef, currentDisplay, master);
}
}
if (false){ // change to true in order to execute the toggle
postError(CGCompleteDisplayConfiguration (configRef,CGConfigureOption.Permanently))
}
// The first entry in the list of active displays is the main display. In case of mirroring, the first entry is the largest drawable display or, if all are the same size, the display with the greatest pixel depth.
// The "Permanently" option might not survive reboot when run from playground, but does when run in an application
}
func toggleMirroringUgly(){
// just to decrease eye strain
typealias displayIDListPtr = UnsafeMutablePointer<CGDirectDisplayID>
typealias configurationRefPtr = UnsafeMutablePointer<CGDisplayConfigRef>
//get count of active displays (by passing nil to CGGetActiveDisplayList
postError(CGGetActiveDisplayList(0, nil, &displayCount))
if displayCount < 2 { return } // no point in any mirroring functions
// ***
// allocate space for array
let displayListPtr = displayIDListPtr.alloc(Int(displayCount)) //see typealias above
// fill the list
postError(CGGetActiveDisplayList(displayCount, displayListPtr, &activeCount))
// ***
// determine if mirroring is active
// hack to convert from boolean_t (aka UInt32) to swift's bool
let displaysMirrored = CGDisplayIsInMirrorSet(CGMainDisplayID()) != 0
// set master based on current mirroring state
// if mirroring master = null, if not, master = main display
let master = (true == displaysMirrored) ? kCGNullDirectDisplay : CGMainDisplayID()
// make room for the configuration reference
let configRefPtr = configurationRefPtr.alloc(1) //see typealias above
// start the configuration
postError(CGBeginDisplayConfiguration (configRefPtr));
for i in 0..<displayCount {
let currentDisplay = CGDirectDisplayID(displayListPtr[Int(i)])
if CGMainDisplayID() != currentDisplay {
CGConfigureDisplayMirrorOfDisplay(configRefPtr[0], currentDisplay, master);
}
}
if (false){ //change to true in order to flip the mirroring
// make it happen
postError(CGCompleteDisplayConfiguration (configRefPtr[0],CGConfigureOption.Permanently));
}
// The first entry in the list of active displays is the main display. In case of mirroring, the first entry is the largest drawable display or, if all are the same size, the display with the greatest pixel depth.
// The "Permanently" option might not survive reboot when run from playground, but does when run in an application
}
toggleMirroring()
Arrays don't necessarily use contiguous storage. There is a ContiguousArray type which you can use if you are so inclined, but you'll still need to deal with the possible difference between your maximum size and the actual size returned after the final call to CGGetActiveDisplayList.
One way of cleaning this up might be to make a custom convenience initializer for Array:
extension Array {
init<Size: IntegerType>(
fillingBufferOfSize maxSize: Size,
#noescape fillBuffer: (buffer: UnsafeMutablePointer<Element>, count: inout Size) throws -> ()) rethrows
{
let maxSizeAsInt = Int(maxSize.toIntMax())
let buf = UnsafeMutablePointer<Element>.alloc(maxSizeAsInt)
defer { buf.dealloc(maxSizeAsInt) }
var actualCount: Size = 0
try fillBuffer(buffer: buf, count: &actualCount)
self.init(UnsafeBufferPointer(start: buf, count: Int(actualCount.toIntMax())))
}
}
Then you can use Array(fillingBufferOfSize: ...):
var maxActive: UInt32 = 0
CGGetActiveDisplayList(0, nil, &maxActive)
let displays = Array(fillingBufferOfSize: maxActive) { (buffer, count) in
CGGetActiveDisplayList(maxActive, buffer, &count)
}
I upgraded my computer with a new video card and NVIDIA drivers and discovered my code above no longer works fully - turns mirroring on but not off. Apparently, there's an option for drivers to use hardware or software mirroring, and that changes the coding. I post below a revised version.
It has only been tested on my system (10.12.2) and card (GTX 980Ti), but I think the logic should accommodate software mirroring and fairly recent OS versions as well. If you have more than 2 displays, you can probably modify it, with heroic effort, to mirror arbitrary combinations. My code will just mirror whatever is considered the main display (or the lowest rez one, in software mirroring) on all the others.
Although jbandes' note re: ContiguousArray was informative, it does not work in this case - see the comments in the code. This code assumes that the allocated array of UInt32s will be contiguous. (Too much work to get fancy with malloc and casting, but this is not production ready.)
Good luck to the 2 people who might be interested!
//: Playground - noun: a place where people can play
import Cocoa
import Foundation
func postError(_ error : CGError){
if error != CGError.success {
print("got an error")
}
}
func disableHardwareMirroring(){
// designed for hardware mirroring with > 1 display
// should be no penalty for running with only 1 display, using either hardware or software mirroring drivers
// but not tested
// start the configuration
var configRef:CGDisplayConfigRef? = nil
postError(CGBeginDisplayConfiguration(&configRef))
// only interested in the main display
// kCGNullDirectDisplay parameter disables hardware mirroring
CGConfigureDisplayMirrorOfDisplay(configRef, CGMainDisplayID(), kCGNullDirectDisplay)
// may not be permanent between boots using Playgroud, but is in an application
postError(CGCompleteDisplayConfiguration (configRef,CGConfigureOption.permanently))
}
func toggleMirroring(){
var displayCount:UInt32 = 0
var activeCount:UInt32 = 0 //used as a parameter, but value is ignored
//var onlineCount:UInt32 = 0 //not used
//get count of active displays (by passing nil to CGGetActiveDisplayList
postError(CGGetActiveDisplayList(0, nil, &displayCount))
if displayCount == 1 {
// either it's hardware mirroring or who cares?
disableHardwareMirroring()
return
}
// allocate space for list of displays
// tried to use ContiguousArray, but CGGetActiveDisplayList requires Array<CGDirectDisplayID> parameter
// ContiguousArrays cannot be typecast to Arrays (at least not easily)
var displayIDList = Array<CGDirectDisplayID>(repeating: kCGNullDirectDisplay, count: Int(displayCount))
// fill the list
postError(CGGetActiveDisplayList(displayCount, &(displayIDList), &activeCount))
// determine if mirroring is active (only relevant for software mirroring)
// hack to convert from boolean_t (aka UInt32) to swift's bool
let displaysMirrored = CGDisplayIsInMirrorSet(CGMainDisplayID()) != 0
// set master based on current mirroring state
// if mirroring, master = null, if not, master = main display
let master = (true == displaysMirrored) ? kCGNullDirectDisplay : CGMainDisplayID()
// start the configuration
var configRef:CGDisplayConfigRef? = nil
postError(CGBeginDisplayConfiguration(&configRef))
for i in 0..<Int(displayCount) {
let currentDisplay = CGDirectDisplayID(displayIDList[i])
if CGMainDisplayID() != currentDisplay {
CGConfigureDisplayMirrorOfDisplay(configRef, currentDisplay, master)
}
}
postError(CGCompleteDisplayConfiguration (configRef,CGConfigureOption.permanently))
// The first entry in the list of active displays is the main display. In case of mirroring, the first entry is the largest drawable display or, if all are the same size, the display with the greatest pixel depth.
// The "Permanently" option might not survive reboot when run from playground, but does when run in an application
}
if (false) { // change to true to run the code, false to edit
toggleMirroring()
}
EDIT: Figured it out using a subroutine and a whole lot of if statements. I've appended the working code to the bottom of the question (since it won't let me post an answer) in case someone comes across a similar issue, with the disclaimer that I'm a mega-novice and the code probably isn't good, just functional.
Thanks for the help, guys!
Original Question:
I'm currently banging my head against an assignment that requires we use the Win32::DriveInfo module to create a list of drives, followed by the drive type. Using an array, we have to take the supplied drive type number and convert it into a descriptive string.
I can get the program to return the appropriate drive type, the problem is converting those numbers into strings. The array is populated based on the drives it finds on the user's system, which means the program has to be able read the array, determine each number, and then (probably?) compare it against some kind of predetermined 'conversion chart' and convert the provided numbers to the appropriate string.
I've been at this for hours, and so far my best option seems like it might be the map function, although we haven't gone over that in class (and my teacher is not fond of googling) so I'm not entirely sure that's what we're supposed to use. And even so, the only information I've been able to find has either been completely over my head, or assumes that the contents of the array are static.
The entire program is longer, but here's the relevant code:
use Win32::DriveInfo;
my #DrivesInUse = Win32::DriveInfo::DrivesInUse();
my #DriveType;
my %DrivesHash;
foreach $DrivesInUse (#DrivesInUse)
{
print "$DrivesInUse: ";
push (#DriveType, Win32::DriveInfo::DriveType($DrivesInUse));
foreach $DriveType (#DriveType)
{
$DrivesHash{$DrivesInUse} = $DriveType;
}
print $DrivesHash{$DrivesInUse} . "\n";
}
And the output is:
A: 2
C: 3
D: 5
E: 5
F: 5
G: 2
Now I just have to figure out a way to convert all potential numbers (0-6) to the appropriate strings and print those instead. We can't use any additional modules, and considering I'm still very much a newbie, the more basic the solution the better.
Edit:
For clarification, the numbers come from the Win32::DriveInfo module, and each of them represent a drive type, description from CPAN below:
0 - the drive type cannot be determined.
1 - the root directory does not exist.
2 - the drive can be removed from the drive (removable).
3 - the disk cannot be removed from the drive (fixed).
4 - the drive is a remote (network) drive.
5 - the drive is a CD-ROM drive.
6 - the drive is a RAM disk.
In my program, I need those numbers to return as descriptive strings instead, so the desired output on my system would be something like:
A: Removable Drive
C: Fixed Drive
D: CD-ROM Drive
E: CD-ROM Drive
F: CD-ROM Drive
G: Removable Drive
My problem is I can't figure out how to convert the numbers in #DriveType to the corresponding strings, since the elements in #DriveType change depending on the system.
I hope that cleared things up?
Working Code
use Win32::SystemInfo;
use Win32::DriveInfo;
my #DrivesInUse = Win32::DriveInfo::DrivesInUse();
my #DriveType;
my %DrivesHash;
foreach $DrivesInUse (#DrivesInUse)
{
print "$DrivesInUse: ";
push (#DriveType, Win32::DriveInfo::DriveType($DrivesInUse));
foreach $DriveType (#DriveType)
{
$DriveString = conversion($DriveType);
$DrivesHash{$DrivesInUse} = $DriveString;
}
print $DrivesHash{$DrivesInUse} . "\n";
}
sub conversion
{
if ($_[0] == 0)
{
$StringContent = "Undetermined";
}
if ($_[0] == 1)
{
$StringContent = "Does not exist";
}
if ($_[0] == 2)
{
$StringContent = "Removable";
}
if ($_[0] == 3)
{
$StringContent = "Fixed";
}
if ($_[0] == 4)
{
$StringContent = "Network";
}
if ($_[0] == 5)
{
$StringContent = "CD-ROM";
}
if ($_[0] == 6)
{
$StringContent = "RAM";
}
return $StringContent;
}
I hope this may help you
use Win32::DriveInfo;
my #DrivesInUse = Win32::DriveInfo::DrivesInUse();
my %DriveType = (
'0' => 'Undetermined',
'1' => 'Does not exist',
'2' => 'Removable',
'3' => 'Fixed',
'4' => 'Network',
'5' => 'CD-ROM',
'6' => 'RAM',
);
for $DrivesInUse ( #DrivesInUse ) {
print "$DrivesInUse: "
. $DriveType{ Win32::DriveInfo::DriveType($DrivesInUse) } . "\n";
}
One way to go could be a types hash like
my %typesHash;
$typesHash{0} = "the drive type cannot be determined.";
$typesHash{1} = "the root directory does not exist.";
.
.
.
edit: In an earlier version i had butchered these lines. Another way to assign the hash would be the following, i just tried to use the version above because i thought it would be clearer and consequently managed to make everything less clear. \o/
my %typesHash = (0 => "the drive type cannot be determined.",
1 => "the root directory does not exist.",
...);
end edit
And later use
print $typesHash{$DrivesHash{$DrivesInUse}} . "\n";
Not necessarily the most elegant solution but a proper use for a hash.
edit:
By the way:
push (#DriveType, Win32::DriveInfo::DriveType($DrivesInUse));
foreach $DriveType (#DriveType)
{
$DrivesHash{$DrivesInUse} = $DriveType;
}
is a completely useless loop. It just assigns all the drive types encountered so far to $DrivesHash{$DrivesInUse} one after the other. It does work because you pushed the most recent one into the array last and so it will end up with the correct one but there is no reason to assign all the others before. If you do not need the array of drive types (they are in the hash as well anyways), the whole stuff pasted above could be replaced with:
$DrivesHash{$DrivesInUse} = Win32::DriveInfo::DriveType($DrivesInUse);
I have a notecard with a different word on each line, and I'd like to be able to select from the lines randomly. How can I do that?
First, as you mention, you need a notecard. For this example, I'm using one named "colors" with the following contents:
red
blue
green
yellow
orange
purple
With that notecard present, the following script will read and chat a random line from the card each time the prim is touched.
//this script will grab and chat a random line from the "colors" notecard each time the prim is touched.
string card = "colors";
key linecountid;
key lineid;
integer linemax;
integer random_integer( integer min, integer max )
{
return min + (integer)( llFrand( max - min + 1 ) );
}
default
{
state_entry()
{
//get the number of notecard lines
linecountid = llGetNumberOfNotecardLines(card);
}
touch_start(integer total_number)
{
lineid = llGetNotecardLine(card, random_integer(0, linemax));
}
dataserver(key id, string data)
{
if (id == linecountid)
{
linemax = (integer)data - 1;
}
else if (id == lineid)
{
llSay(0, data);
}
}
}
It's not clear why you do such unnecessary math with adding one and then substracting it again later in the answer you give yourself above.
If you want to make sure you have a more random number as there are known issues with the randomness of llFrand you could do (without checking whether the number is even or odd):
integer max;
integer random = llFrand((integer)(max/2)) + llFrand((integer)(max/2));
The second issue with your code above is that you are not checking against CHANGED_INVENTORY and I'm not quite sure why you'd not do that. Following up on this second issue, why do you ask a question to get a random notecard line number and give an answer that provides a random within a range? And what will you do if the notecard changes? Change the code and the notecard? This seems to be redundant to me.
NOTECARD named colors or whatever you set in the script:
blue
red
green
yellow
black
SCRIPT within the same prim:
// this script reads from a notecard which is named whatever you set in init
// in this example from a notecard named "colors"
string ncName;
key ncNumOfLinesReqId;
key ncReqId;
integer numOfLines;
init()
{
// Put the name of your notecard as in the prim's inventory here.
ncName = "colors";
}
default
{
changed(integer change)
{
// reset script to make sure you have the current number of lines
// CHANGED_OWNER has the integer value 0x80 (128)
// CHANGED_INVENTORY has the integer value 0x01 (1)
if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
{
llResetScript();
}
}
state_entry()
{
init();
// get the number of notecard lines
ncNumOfLinesReqId = llGetNumberOfNotecardLines(ncName);
}
touch_start(integer num_detected)
{
// if the number of lines is 0
if (!numOfLines)
{
// PUBLIC_CHANNEL has the integer value 0
llSay(PUBLIC_CHANNEL, "~!~ Unconfigured, check notecard ~!~");
}
else // if number of lines not 0
{
ncReqId = llGetNotecardLine(ncName, (integer)llFrand(numOfLines));
}
}
dataserver(key reqId, string data)
{
if (reqId == ncNumOfLinesReqId)
{
// make sure you typecast!
numOfLines = (integer)data;
}
else if (reqId == ncReqId)
{
// PUBLIC_CHANNEL has the integer value 0
llSay(PUBLIC_CHANNEL, data);
}
}
}
Further information:
The notecard you are reading from does not necessarily have to be in the same prim. If you know the UUID of the notecard, you can read from it as long as it's transferable (and not deleted). Be warned that changing the contents of the notecard and saving, stores the new content under a different UUID. But if you're that skilled, you might as well store the text on a web service and get the text snippet count and text snippets from there.
More on the official Second Life wiki.