The snippet below detects from a list of files which of them is a Directory on Ftp
as C# it will be like below
var files = new List<string>(){"App_Data", "bin", "Content"};
var line = "drwxr-xr-x 1 ftp ftp 0 Mar 18 22:41 App_Data"
var dir = files.First(x => line.EndsWith(x));
How I can transalte the last line in PowerShell ?
Something like this...
$files = #("App_Data", "bin", "Content")
$line = "drwxr-xr-x 1 ftp ftp 0 Mar 18 22:41 App_Data"
$dir = $files | Where { $line.EndsWith($_) } | Select -First 1
These versions of the last line would all accomplish the same:
$dir = #($files | Where { $line.EndsWith($_) })[0]
$dir = $files | Where { $line.EndsWith($_) } | Select -index 0
$dir = $files | Where { $line.EndsWith($_) } | Select -First 1
It was pointed out that the above is not exactly equivalent in behavior to Linq.First because Linq.First throws exceptions in two cases:
Throws ArgumentNullException when source or predicate is null.
Throws InvalidOperationException when source sequence is empty or no element satisfies the condition in predicate.
If you wanted that behavior exactly, you'd need some extra guard code.
as Robert Groves said, Select-Object -First Occurence do the tricks, you can also use -Last Occurence.
by the way, like any other static .Net method you can use linq in powershell.
[Linq.Enumerable]::First($list)
[Linq.Enumerable]::Distinct($list)
[Linq.Enumerable]::Where($list, [Func[int,bool]]{ param($item) $item -gt 1 })
Doug Finke produced a great video ( only 7 mins ) about converting C# to Powershell
http://dougfinke.com/video/CSharpToPowerShell.html
Roberts example is very good indeed, though comma delimiting will implicitly be treated as an array
the shortest way of doing it would be to put it all into a single pipeline :
$dir = "App_Data", "bin", "Content" | % { if("drwxr-xr-x 1 ftp ftp 0 Mar 18 22:41 App_Data".EndsWith($_)) { $_ } } | select -first 1
There is a native way to do this using the Powershell Array's Where Function by passing in a WhereOperatorSelectionMode like this:
(1..9).Where({ $_ -gt 3}, "First") # 4
You can also use the mode straight from the enum as well:
$mode = [System.Management.Automation.WhereOperatorSelectionMode]::First
(1..9).Where({ $_ -gt 3}, $mode) # 4
Using any of the values from the WhereOperatorSelectionMode enum
Name
Val
Description
Default
0
Return all items
First
1
Return the first item
Last
2
Return the last item
SkipUntil
3
Skip items until condition is true
Until
4
Return all items until condition is true
Split
5
Return an array of two elements
See Also: Checking Out The Where and ForEach Operators in PowerShell V4
This is a really simple implementation for First:
function First($collection)
{
foreach ($item in $collection)
{
return $item
}
return $null
}
Instead of returning $null, you could throw an InvalidOperationException exception.
Related
I'm currently making a Game and I have already split the string everything right but how I can trim the string and output the last line or a line in the middle?
Code:
text = "Username:King100 ID:100 Level:10";
string[] splittext = text.Split(' ');
foreach (var texs in splittext)
{
Console.WriteLine(texs);
}
Output:
Username:King100
ID:100
Level:10
I just want display the level 10 in the Console how thats works?
thanxs for helping.
Edit: the level can be changed often like 10 or 100 or 1000
Regex is more flexible solution. But if your text format is contsant, you can use this simple way:
string level = text.Substring(text.LastIndexOf(':') + 1);
You can also use a Regular Expression to solve this:
var regex = new Regex(#"Level:(?<Level>\d*)");
var matches = regex.Matches("Username:King100 ID:100 Level:10");
if (matches.Count > 0 && matches[0].Success)
{
Console.WriteLine(matches[0].Groups["Level"].Value);
}
var text = "Username:King100 ID:100 Level:10";
/*
Splits the given string on spaces and then splits on ":"
and creates a Dictionary ("Dictionary<TKey, TValue>")
*/
var dict = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(part => part.Split(':'))
.ToDictionary(split => split[0], split => split[1]);
//If the dictionary count is greater than Zero
if(dict.Count > 0)
{
var levelValue = dict["Level"].ToString();
}
OK, because i'm annoying and totally bored of work, i decided to benchmark everyone's solutions.
The premise was simply to make an array of 1000 (scale) lines of strings (in the given format) with random positive int on the end;
Note : I made every solution int.Parse the result, as it seemed more useful
Mine
This just uses fixed, unsafe, pointers and no error checking
var level = 0;
fixed (char* pitem = item)
{
var len = pitem + item.Length;
for (var p = pitem ; p < len; p++)
if (*p >= '0' && *p <= '9')
level = level * 10 + *p - 48;
else
level = 0;
}
Results
Mode : Release
Test Framework : .NET Framework 4.7.1
Benchmarks runs : 1000 times (averaged)
Scale : 1,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
--------------------------------------------------------------------------
Mine | 0.095 ms | 0.085 ms | 0.01 | 317,205 | Yes | 96.59 %
Sanan | 0.202 ms | 0.184 ms | 0.02 | 680,747 | Yes | 92.75 %
Zaza | 0.373 ms | 0.316 ms | 0.10 | 1,254,302 | Yes | 86.60 %
Kishore | 0.479 ms | 0.423 ms | 0.06 | 1,620,756 | Yes | 82.81 %
Hussein | 1.045 ms | 0.946 ms | 0.11 | 3,547,305 | Yes | 62.50 %
Maccettura | 2.787 ms | 2.476 ms | 0.39 | 9,474,133 | Base | 0.00 %
Hardkoded | 6.691 ms | 5.927 ms | 0.67 | 22,750,311 | Yes | -140.09 %
Tom | 11.561 ms | 10.635 ms | 0.78 | 39,344,419 | Yes | -314.80 %
Summary
All the solutions do different things in different ways, comparing them is not really apples to apples.
Don't use mine, its totally unrealistic and only for fun. Use the version that makes the most sense to you, that is the most robust and easiest to maintain.
As always, regex is the slowest.
If level is always the last part of the string, and all you care about is the actual number, then you can just do:
var level = text.Split(':').LastOrDefault();
This would just split on ':' and give you the last (or default) element. Given your example input, level = "10".
Try this:
string input = "Username:King100 ID:100 Level:10";
Match m = Regex.Match(input, #"\s*Level:(?<level>\d+)");
if (m.Success&& m.Groups["level"].Success)
Console.WriteLine(m.Groups["level"].Value);
Also works for:
string input = "Username:King100 Level:10 ID:100";
string text = texs.Substring(texs.IndexOf("Level:")+6);
System.Console.WriteLine(text);
I'm trying to parse C# preprocessors using ANTLR4 instead of ignoring them. I'm using the grammar mentioned here: https://github.com/antlr/grammars-v4/tree/master/csharp
This is my addition (now i'm focusing only on pp_conditional):
pp_directive
: Pp_declaration
| pp_conditional
| Pp_line
| Pp_diagnostic
| Pp_region
| Pp_pragma
;
pp_conditional
: pp_if_section (pp_elif_section | pp_else_section | pp_conditional)* pp_endif
;
pp_if_section:
SHARP 'if' conditional_or_expression statement_list
;
pp_elif_section:
SHARP 'elif' conditional_or_expression statement_list
;
pp_else_section:
SHARP 'else' (statement_list | pp_if_section)
;
pp_endif:
SHARP 'endif'
;
I added its entry here:
block
: OPEN_BRACE statement_list? CLOSE_BRACE
| pp_directive
;
i'm getting that error:
line 19:0 mismatched input '#if TEST\n' expecting '}'
when i use the following test case:
if (!IsPostBack){
#if TEST
ltrBuild.Text = "**TEST**";
#else
ltrBuild.Text = "**LIVE**";
#endif
}
The problem is that a block is composed of either '{' statement_list? '}' or a pp_directive. In this specific case, it chooses the first, because the first token it sees is a { (after the if condition). Now, it is expecting to maybe see a statement_list? and then a }, but what it find is #if TEST, a pp_directive.
What do we have to do? Make your pp_directive a statement. Since we know statement_list: statement+;, we search for statement and add pp_directive to it:
statement
: labeled_statement
| declaration_statement
| embedded_statement
| pp_directive
;
And it should be working fine. However, we must also see if your block: ... | pp_directive should be removed or not, and it should be. I'll let it for you to find out why, but here's a test case that's ambiguous:
if (!IsPostBack)
#pragma X
else {
}
I'm reading content from two files, now I want to test that content with my expected string.
string read1 = File.ReadAllText("#C:\somefile.txt");
string read2 = File.ReadAllText("#C:\somefilee.txt");
string expectedString = "blah";
Assert.AreEqual(read1 and read2 equals expected );
I know this is basic but I'm kinda stuck here.
You need to use 2 asserts, first to compare expected string with first file content, and then compare second file content with the first one (or with expected string once again), e.g.:
Assert.AreEqual(expectedString, read1, "File content should be equal to expected string");
Assert.AreEqual(read1, read2, "Files content should be identical");
Or you can use the condition
Assert.IsTrue(read1 == read2 == expectedString, "Files content should be equal to expected string");
But in this case you won't know what was the problem if the test fails.
I prefer to use plain C# to write such assertions, which you can with ExpressionToCode (nuget package). With that, your assertion would look as follows:
PAssert.That(
() => read1 == expectedString && read2 == expectedString
, "optional failure message");
On a failure, the library will include that expression in it's output, and include the actual values of the various variables (read1, read2, and expectedString) you've used.
For example, you might get a failure that looks as follows:
optional failure message
read1 == expectedString && read2 == expectedString
| | | | | | |
| | | | | | "blah"
| | | | | false
| | | | "Blah"
| | | false
| | "blah"
| true
"blah"
Disclaimer: I wrote ExpressionToCode.
Assert(read1 == read2 && read1 == expectedString, "Not all equal")
If I get you right, you want this:
try{
if(Assert.AreEqual(read1,read2,false)){
//do things
}
catch(AssertFailedException ex){
//assert failed
}
Look here for MSDN.
How can I rename multiple files like this:
file.txt , anotherfile.txt , log.txt
into something like this :
file1.txt , file2.txt , file3.txt
How can I do this in c# or in c++ ?
Use File.Move Method as:
IEnumerable<FileInfo> files = GetFilesToBeRenamed();
int i = 1;
foreach(FileInfo f in files)
{
File.Move(f.FullName, string.Format("file{0}.txt", i));
i++;
}
And if f is a fullpath, then you can do this instead:
File.Move(f.FullName,
Path.Combine(f.Directory.ToString(), string.Format("file{0}.txt", i));
This would work in you're using an sh-based shell:
#!/bin/sh
FEXT="txt" # This is the file extension you're searching for
FPRE="file" # This is the base of the new files names file1.txt, file2.txt, etc.
FNUM=1; # This is the initial starting number
find . -name "*.${FEXT}" | while read OFN ; do
# Determine new file name
NFN="${FPRE}${FNUM}.${FEXT}"
# Increment FNUM
FNUM=$(($FNUM + 1))
# Rename File
mv "${OFN}" "${NFN}"
done
The script in action:
[james#fractal renfiles]$ touch abc.txt
[james#fractal renfiles]$ touch test.txt
[james#fractal renfiles]$ touch "filename with spaces.txt"
[james#fractal renfiles]$ ll
total 4
-rw-rw-r-- 1 james james 0 Sep 3 17:45 abc.txt
-rw-rw-r-- 1 james james 0 Sep 3 17:45 filename with spaces.txt
-rwxrwxr-x 1 james james 422 Sep 3 17:41 renfiles.sh
-rw-rw-r-- 1 james james 0 Sep 3 17:45 test.txt
[james#fractal renfiles]$ ./renfiles.sh
[james#fractal renfiles]$ ll
total 4
-rw-rw-r-- 1 james james 0 Sep 3 17:45 file1.txt
-rw-rw-r-- 1 james james 0 Sep 3 17:45 file2.txt
-rw-rw-r-- 1 james james 0 Sep 3 17:45 file3.txt
-rwxrwxr-x 1 james james 422 Sep 3 17:41 renfiles.sh
In c++, you will eventually use
std::rename(frompath, topath);
to perform the action. TR2 proposal N1975 covers this function. However, until then, use boost::rename for the immediate future, and tr2::rename for the period after approval before final placement.
Loop through and use whatever names you want. Don't quite know if you're trying to add numbers, because the current question says 1, 2, 2.
In c# you can use File.Move(source, dest)
Of course you can do it programatically:
string[] files = new string[] {"file.txt" , "anotherfile.txt" , "log.txt"}:
int index = 0;
string Dest;
foreach (string Source in files)
{
if (!Files.Exists(Source )) continue;
do {
index++;
Dest= "file"+i+".txt";
} while (File.Exists(NewName);
File.Move(Source , Dest);
}
Here is a simple scenario in C#:
var intList = new List<int>();
intList.Add(4);
intList.Add(7);
intList.Add(2);
intList.Add(9);
intList.Add(6);
foreach (var num in intList)
{
if (num == 9)
{
intList.Remove(num);
Console.WriteLine("Removed item: " + num);
}
Console.WriteLine("Number is: " + num);
}
This throws an InvalidOperationException because I am modifying the collection while enumerating it.
Now consider similar PowerShell code:
$intList = 4, 7, 2, 9, 6
foreach ($num in $intList)
{
if ($num -eq 9)
{
$intList = #($intList | Where-Object {$_ -ne $num})
Write-Host "Removed item: " $num
}
Write-Host "Number is: " $num
}
Write-Host $intList
This script actually removes the number 9 from the list! No exceptions thrown.
Now, I know the C# example uses a List object while the PowerShell example uses an array, but how does PowerShell enumerate a collection that will be modified during the loop?
The foreach construct evaluates the list to completion and stores the result in a temporary variable before it starts iterating over it. When you do that actual removal you are updating $intList to reference a new list. In other words in actually doing something like this under the hood:
$intList = 4, 7, 2, 9, 6
$tempList=$intList
foreach ($num in $tempList)
{
if ($num -eq 9)
{
$intList = #($intList | Where-Object {$_ -ne $num})
Write-Host "Removed item: " $num
}
Write-Host "Number is: " $num
}
Write-Host $intList
Your call to:
$intList = #($intList | Where-Object {$_ -ne $num})
Actually creates a completely new list with the value removed.
If you change the removal logic to remove the last item in the list (6) then I think you'll find that it's still printed even though you think it's removed because of the temporary copy.
The answer is already given by #Sean, I am just providing the code which shows that the original collection is not changed during foreach: it enumerates through the original collection and there is no contradiction therefore.
# original array
$intList = 4, 7, 2, 9, 6
# make another reference to be used for watching of $intList replacement
$anotherReferenceToOriginal = $intList
# prove this: it is not a copy, it is a reference to the original:
# change [0] in the original, see the change through its reference
$intList[0] = 5
$anotherReferenceToOriginal[0] # it is 5, not 4
# foreach internally calls GetEnumerator() on $intList once;
# this enumerator is for the array, not the variable $intList
foreach ($num in $intList)
{
[object]::ReferenceEquals($anotherReferenceToOriginal, $intList)
if ($num -eq 9)
{
# this creates another array and $intList after assignment just contains
# a reference to this new array, the original is not changed, see later;
# this does not affect the loop enumerator and its collection
$intList = #($intList | Where-Object {$_ -ne $num})
Write-Host "Removed item: " $num
[object]::ReferenceEquals($anotherReferenceToOriginal, $intList)
}
Write-Host "Number is: " $num
}
# this is a new array, not the original
Write-Host $intList
# this is the original, it is not changed
Write-Host $anotherReferenceToOriginal
Output:
5
True
Number is: 5
True
Number is: 7
True
Number is: 2
True
Removed item: 9
False
Number is: 9
False
Number is: 6
5 7 2 6
5 7 2 9 6
We can see that $intList is changed when we "remove an item". It only means that this variable now contains a reference to a new array, it is the variable changed, not the array. The loop continues enumeration of the original array which is not changed and $anotherReferenceToOriginal still contains a reference to it.
The problem here is that you're not comparing equivalent code samples. In the Powershell sample you are creating a new list vs modifying the list in place as is done in the C# sample. Here is a sample which is closer in functionality to the original C# one
$intList = new-object System.Collections.ArrayList
$intList.Add(4)
$intList.Add(7)
$intList.Add(2)
$intList.Add(9)
$intList.Add(6)
foreach ($num in $intList) {
if ($num -eq 9) {
$intList.Remove($num)
Write-Host "Removed item: " $num
}
Write-Host "Number is: " $num
}
Write-Host $intList
And when run it produces the same error
Number is: 4
Number is: 7
Number is: 2
Removed item: 9
Number is: 9
An error occurred while enumerating through a collection: Collection was modifi
ed; enumeration operation may not execute..
At C:\Users\jaredpar\temp\test.ps1:10 char:8
+ foreach <<<< ($num in $intList)
+ CategoryInfo : InvalidOperation: (System.Collecti...numeratorSi
mple:ArrayListEnumeratorSimple) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
4 7 2 6